Heroku App: https://data-centric-dev-project.herokuapp.com
Heroku git: https://git.heroku.com/data-centric-dev-project.git
GitHub: https://github.com/bennettpe/data-centric-dev-website.git
This is the milestone project that I have created for the “Data Centric Development” module, which is part of “Full Stack Web Development Course” offered by Code Institute.
This project will be built using data-driven application technologies which have been learnt within the Data Centric Development module.
-
CREATE AN ONLINE COOKBOOK
- Create a web application that allows users to store and easily access cooking recipes.
- Put some effort into designing a database schema based on recipes, and any other related properties and entities
(e.g. views, upvotes, ingredients, recipe authors, allergens, author’s country of origin, cuisine etc.…).
Make sure to put some thought into the relationships between them, and use either foreign keys (in the case of a relational database)
or nesting (in the case of a document store) to connect these pieces of data. - Create the backend code and frontend form to allow users to add new recipes to the site (at least a basic one, if you haven’t taken the frontend course).
- Create the backend code to group and summarize the recipes on the site, based on their attributes such as cuisine,
country of origin, allergens, ingredients, etc.
and a frontend page to show this summary, and make the categories clickable to drill down into a filtered view based on that category.
This frontend page can be as simple or as complex as you’d like; you can use a Python library such as matplotlib,
or a JS library such as d3/dc (that you learned about if you took the frontend modules) for visualization - Create the backend code to retrieve a list of recipes, filtered based on various criteria
(e.g. allergens, cuisine, etc.…) and order them based on some reasonable aspect (e.g. number of views or upvotes).
Create a frontend page to display these, and to show some summary statistics around the list (e.g. number of matching recipes, number of new recipes.
Optionally, add support for pagination, when the number of results is large. - Create a detailed view for each recipes, that would just show all attributes for that recipe, and the full preparation instructions.
- Allow for editing and deleting of the recipe records, either on separate pages, or built into the list/detail pages.
- Optionally, you may choose to add basic user registration and authentication to the site.
This can as simple as adding a username field to the recipe creation form, without a password (for this project only, this is not expected to be secure)
-
CREATE YOUR OWN PROJECT
- If you choose to create your project outside the brief, the scope should be similar to that of the example brief above. If you want some ideas, please ask your mentor for advice and direction..
A web application online cookbook , recipes for the cookbook have been gathered from the following website goodfood.com
- The user has a choice to do the following
- Create an account with a username and password
- Sign-in with a registered username and password
- Search recipes (by without an allergen)
- Search recipes (by category)
- Search recipes (by cuisine)
- Search recipes (by difficulty)
- Search recipes (by main ingredient)
- View recipe details
- Vote to like / dislike recipe
- Add recipe (by registered user)
- Delete recipe (by registered user)
- Edit recipe (by registered user)
- View my recipes (by registered user)
- A website that allows users to store and easily access cooking recipes.
To provide a online cookbook that users must be able to do the following
- Allow users to add new recipes.
- Group and summarize recipes.
- Retrieve a list of recipes.
- Show all attributes for that recipe.
- Allow editing and deleting of recipe records.
- Add basic user registration and authentication.
- Ensure there's a README.md.
- A way for users to create a unique username via a login facility.
- Create front-end form and back-end code to allow users to add new recipes to website.
- Create Front-end page to group and summarize recipes based on there attributes (e.g. cuisine, country of origin, allergens, ingredients etc...).
- Create back-end code to retrieve a list of recipes and filtered on various criteria (e.g. allergens, cuisine etc...) and order them based on some reasonable aspect (e.g. number of views or votes).
- Create front-end form and back-end code to allow users to edit recipes on website.
For planning see Planning documentation
For wireframe see Wireframe documentation
My MongoDB database consists of the following collections
- allergens
- categories
- cuisines
- difficulties
- main_ingredients
- recipes
- users
Diagram of website database schema
Users will access website via /base
On the navbar there are the following four icons (Home, All recipes, Sign in & register) and on the page there are Register and sign in buttons.
Unregistered users can search recipes by allergens, category, difficulty, main ingredient and 16 cuisine cards where they can view cuisine recipes and also like, dislike recipes.
register button allows uses to register username and password.
sign in button allows users to sign in
once signed in the additional icons are shown in the navbar for add recipes, my recipes and sign out, also buttons add recipes and my recipes are shown on the page.
Diagram of website functional flow
Technologies used in the construction of this project include,
- Atom A hackable text editor for the 21st Century.
- Bootstrap is a framework for building responsive, mobile-first websites.
- Bson is a computer data interchange format used mainly as a data storage and network transfer format in the MongoDB database.
- Chrome Developer Tools is a set of web developer tools built directly into the Google Chrome browser.
- 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.
- Flask a microframework for Python based on Werkzeug, Jinja 2.
- Flask Bcrypt is a Flask extension that provides bcrypt hashing utilities for your application.
- Flask Microframework is a microframework for Python.
- Flask PyMongo Bridges between Flask and PyMongo.
- Flask Session Flask-Session is an extension for Flask that adds support for Server-side Session to your application.
- Flask WTF Simple integration of Flask and WTForms, including CSRF, file upload, and reCAPTCHA..
- 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.
- GitIgnore is a web service designed to help you create .gitignore files for your Git repositories.
- 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.
- Jinga2 a full featured template engine for Python.
- Pencil is an open-source GUI prototyping tool used to create Wireframe mockup.
- Python 3.4.3 is a scripting language.
- PyMongo is a MongoDB driver for Python used to access the MongoDB database.
- Slack is a collaboration hub that connects your organization
- StartBootstrap Free Bootstrap themes and templates.
- Werkzeug a WSGI utility library for Python.
- WTforms a framework agnostic library handling web forms in python.
Make sure once the workspace has been created in Cloud9 you create the following for git by typing the following commands,
git init
to create a empty Git repository.- Create
.gitignore
which specifies intentionally untracked files to ignore - Add to
.gitignore
file the ignore file(s) for the environments you are using in your workspace i.e.Cloud9
,Flask
,Python
by copying ignore files from GitIgnore - Also make sure you add your own personal non environmental file you would like to be excluded as well.
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
MarkupSafe==1.1.0
Werkzeug==0.14.1
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__"
# 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 Clould9 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://data-centric-development-project-bennettpe.c9users.io. and your should see the following in your browser which confirms everything is running OK.
Hello Flask
-
Clould9 comes with Mongo support built-in but is currently an old version so needs to be upgraded to the newest version (currently 3.4.18) by typing the following command,
wget -q https://git.io/vFb1J -O /tmp/setupmongodb.sh && source /tmp/setupmongodb.sh
Messages from terminal
The following extra packages will be installed:
mongodb-org-mongos mongodb-org-shell mongodb-org-tools
The following NEW packages will be installed:
mongodb-org mongodb-org-mongos mongodb-org-server mongodb-org-shell
mongodb-org-tools
0 upgraded, 5 newly installed, 0 to remove and 298 not upgraded.
-
To connect your MongoDB database with Flask application which will allow your application to programmatically perform CRUD operations you need to install flask-pymongo, This will install the Python 3 version of Flask-PyMongo.
by typing the following command,
sudo pip3 install flask_pymongo
Successfully installed flask-pymongo
Cleaning up...
- Add a Bson and PyMongo to your code:
from bson.objectid import ObjectId
from flask_pymongo import PyMongo
-
To work with MongoDB programmatically using Python we need to install PyMongo which is a driver for Python to access the MongoDB database, First we need to install some additional libraries.
by typing the following command,
sudo apt-get install build-essential python-dev
Messages from terminal
The following NEW packages will be installed:
libpython-dev libpython2.7-dev python-dev python2.7-dev
The following packages will be upgraded:
libpython2.7 libpython2.7-minimal libpython2.7-stdlib python2.7
python2.7-minimal
5 upgraded, 4 newly installed, 0 to remove and 293 not upgraded.
Then we can install PyMongo ,This will install the Python 3 version of PyMongo
by typing the following command,
sudo pip3 install pymongo
Messages from terminal
Successfully installed pymongo
Cleaning up...
To create bBcrypt hashing facility , I decided to use Flask-Bcrypt as per pretty you tube videos.
by typing the following command, sudo pip3 install bcrypt --user
Messages from terminal
Successfully installed bcrypt cffi pycparser
Cleaning up...
- Add a Bcrypt to your code:
from flask_bcrypt import Bcrypt
bcrypt = Bcrypt(app)
To create the Register and Sign in user forms , I decided to use Flask-WTF which is a Python visualization package as it seems easy to integrate into the application
by typing the following command
sudo pip3 install flask-wtf
Messages from terminal
Successfully installed flask-wtf WTForms
Cleaning up...
To create a new database we need to
- log in to mLab https://mlab.com/welcome/using using your log in credentials.
- or create your account if you don't have a log in.
New database created called online_cookbook
To connect using mongo shell
mongo ds229790.mlab.com:29790/online_cookbook -u <dbuser> -p <dbpassword>
To connect using driver via the standard MongoDB URI
app.config["MONGO_URI"] = 'mongodb://<dbuser>:<dbpassword>@ds213665.mlab.com:13665/online_cookbook'
To connect MongoDB database online_cookbook
via flask application we need to do the following.
In your https://mlab.com/home your see MongoDB Deployments
- Click on deployment for online_cookbook
- Click on Users
- Click on Add database user
- Add Database username, Database password, Confirm password and click create
In Flask add the following configuration code after theapp = Flask(__name__)
line.
app.config["MONGO_DBNAME"] = 'task_manager'
app.config["MONGO_URI"] = 'mongodb://<dbuser>:<dbpassword>@ds213665.mlab.com:13665/online_cookbook'
mongo = PyMongo(app)
So once the database user has been created in "Connect Flask To MongoDB section" you can create the Collection(s) as follows
- Click on Collections
- Add Collection name and click create
I decide to download and use the following free one page boostrap theme (Agency) 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-agency/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-agency-gh-pages - Type
mv startbootstrap-agency-gh-pages/css startbootstrap-agency-gh-pages/img startbootstrap-agency-gh-pages/js startbootstrap-agency-gh-pages/scss startbootstrap-agency-gh-pages/vendor
which will Move the files needed into the following file directories CSS,IMG,JS,SCSS,VENDOR - Type
rm -rf startbootstrap-agency-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 Clould9 environment.
├── static
│ ├── css # Bootstrap files
│ │ │
│ │ ├── agency.min.css
│ │ └── my-creative.css # my css file
│ │
│ ├── images
│ │ └── website # cookbook image files
│ │
│ ├── img # Website category images
│ ├── js # Bootstrap files
│ ├── scss # Bootstrap files
│ ├── vendor # Vendor files
│ └── wireframe # Wireframe files
│
├── templates # html templates
│ ├── add_recipe.html # add recipe form
│ ├── base.html # base recipe page
│ ├── by_category.html # recipes by category search
│ ├── by_cuisine.html # recipes by cuisine search
│ ├── by_difficulty.html # recipes by difficulty search
│ ├── by_main_ingredient.html # recipes by main ingredient search
│ ├── by_my_recipes.html # recipes by username search
│ ├── edit_recipes.html # edit recipe form
│ ├── footer.html # footer
│ ├── head.html # links
│ ├── list_all_recipes.html # list all recipes page
│ ├── messaging.html # flash messages
│ ├── navbar.html # navbar
│ ├── not_by_allergen.html # recipes not by allergen search
│ ├── register_user.html # register user
│ ├── script.html # scripts
│ ├── view_details_recipe.html # view recipe details
│ └── view_my_details_recipe.html # view user recipe details
│
├── .gitignore # Git ignore file
├── app.py # Flask file
├── forms.py # WTF forms file
├── mongo.py # Manual unit testing file(s)
├── procfile # App dynos Heroku file
├── README.md # Readme file
├── requirements.txt # python packages file
└── secretkey.py # Create secretkey
The build data to be loaded into MongoDB were created in .json file(s) in the data directory folder
data-centric-development-project
└── static
└── data
└── json
├── allergens.json # Allergen File
├── categories.json # Category File
├── cuisines.json # Cuisine File
├── difficulties.json # Difficulty File
├── main_ingredients.json # Main ingredient File
└── recipes.json # Sample Recipes File
Connect MongoDB database online_cookbook
via flask application.
by typing the following command which imports the .json file,
`mongoimport -h ds213665.mlab.com:13665 -d online_cookbook -c cuisines -u <user> -p <password> --file ./static/data/json/cuisines.json`
Messages from terminal <br>
if you get this message
Failed: error unmarshaling bytes on document #0: JSON decoder out of sync - data changing underfoot?
Then the file is an array and you need to add --jsonArray
to end of command.
connected to: ds213665.mlab.com:13665
imported 14 documents
The .json file(s) were created as follows,
With the following Column variables
allergens.json
{"allergen_name":"Celery"}
categories.json
{"category_name":"Afternoon tea"}
cuisines.json
[{"cuisine_name":"American",
"cuisine_image":"https://www.bbcgoodfood.com/sites/default/files/recipe-collections/collection-image/2013/05/buffalo-wings.jpg"}]
difficulties.json
{"difficulty_name":"Easy"}
main_ingredients.json
{"main_ingredient":"Bacon"}
recipe.json
{"allergen_name": "Peanuts",
"author_name": "Elena Silcock",
"category_name": ["Vegetarian", "Vegan", "Gluten-free"],
"cooking_time": "45 Minutes",
"cuisine_name": "Indian",
"difficulty_name": "Easy",
"main_ingredient": "Potatoes",
"preparation_time": "15 Minutes",
"recipe_description": ["Cook this tasty vegan curry for an exotic yet easy family dinner.",
"With spinach and sweet potato, it boasts two of your five-a-day and it,s under 400 calories"],
"recipe_image": "https://www.bbcgoodfood.com/sites/default/files/styles/recipe/public/recipe/recipe-image/2017/07/satay-sweet-potato-curry.jpg?itok=bl5QzsfL",
"recipe_ingredents": ["1 tbsp coconut oil",
"1 onion, chopped",
"2 garlic cloves, grated",
"thumb-sized piece ginger, grated",
"3 tbsp Thai red curry paste",
"1 tbsp smooth peanut butter",
"500g sweet potato",
"Sweet potatoes",
"peeled and cut into chunks,400ml can coconut milk",
"Coconut milk in a glass",
"with half a coconut",
"200g bag spinach",
"1 lime, juiced",
"cooked rice, to serve (optional)",
"dry roasted peanuts, to serve (optional)"],
"recipe_method": ["1. Melt 1 tbsp coconut oil in a saucepan over a medium heat and soften 1 chopped onion for 5 mins. Add 2 grated garlic cloves and a grated thumb-sized piece of ginger, and cook for 1 min until fragrant.",
"2. Stir in 3 tbsp Thai red curry paste, 1 tbsp smooth peanut butter and 500g sweet potato, peeled and cut into chunks, then add 400ml coconut milk and 200ml water.",
"3. Bring to the boil, turn down the heat and simmer, uncovered, for 25-30 mins or until the sweet potato is soft.",
"4. Stir through 200g spinach and the juice of 1 lime, and season well. Serve with cooked rice, and if you want some crunch, sprinkle over a few dry roasted peanuts."],
"recipe_name": "Satay sweet potato curry",
"ratings_score": 106,
"servings_num": 4,
"username": "tiggerbloggs"}
These files(s) were validated using the following tool CsvJson before loading into mongodb using mongoimport
command.
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.
Testing connection to MongoDB from Flask
I connected to the MongoDB Database:online_cookbook via the mongo shell to check connect was good - PASSED
I did notice that the versions of MongoDB shell and MongoDB server did not match after applying changes as per video's
Have raised comment on Slack.
The following response came back on 07/02/19 when someone else raised the same question,
Just replace the numbers with the version you want.
sudo apt-get install -y mongodb-org=4.0.6 mongodb-org-server=4.0.6 mongodb-org-shell=4.0.6 mongodb-org-mongos=4.0.6 mongodb-org-tools=4.0.6
MongoDB shell version v3.4.18
connecting to: mongodb://ds213665.mlab.com:13665/online_cookbook
MongoDB server version: 3.6.9
WARNING: shell and server versions do not match
Welcome to the MongoDB shell.
I ran the connection test as per mLab documentation https://docs.mlab.com/#load-data
Ran the following commands which
- Created db.mynewcollection and inserted "foo" : "bar"
- Confirm that the shell output matches after amending "_id"
rs-ds213665:PRIMARY> db.mynewcollection.insert({ "foo" : "bar" })
WriteResult({ "nInserted" : 1 })
rs-ds213665:PRIMARY> db.mynewcollection.find()
{ "_id" : ObjectId("5c4d6817b30a5f0e694dee60"), "foo" : "bar" }
rs-ds213665:PRIMARY> { "_id" : ObjectId("5c4d6817b30a5f0e694dee60"), "foo" : "bar" }
Testing Flask-Bcrypt
I needed to make sure that the hashing the password worked and reading a hashed password worked ok.
I ran the following checks as per Corey Schafer youtube video
Python Flask Tutorial: Full-Featured Web App Part 6 - User Authentication
https://youtu.be/CSHx6eCkmv0
Ran the following commands after logging into python
- >>> from flask_bcrypt import Bcrypt
- >>> bcrypt = Bcrypt()
To generate a hashed password of 'testings' adding (.decode('utf-8')) creates a string.
- >>> hashed_pw = bcrypt.generate_password_hash('testings').decode('utf-8')
To check if hashed password = password of 'testings'
- >>> bcrypt.check_password_hash(hashed_pw, 'testings'.encode('utf-8'))
>>> bennettpe:~/workspace (master) $ python3
Python 3.4.3 (default, Nov 17 2016, 01:08:31)
[GCC 4.8.4] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from flask_bcrypt import Bcrypt
>>> bcrypt = Bcrypt()
>>> bcrypt.generate_password_hash('testings').decode('utf-8')
'$2b$12$e9dwKKM2CqbA0z4WDzNYWeGo4CI2FGB7l0UmK0OjSiLWp9MsUXW1y'
>>> hashed_pw = bcrypt.generate_password_hash('testings').decode('utf-8')
>>> bcrypt.check_password_hash(hashed_pw, 'password')
False
>>> bcrypt.check_password_hash(hashed_pw, 'testings')
True
>>> bcrypt.check_password_hash(hashed_pw, 'testings'.encode('utf-8'))
True
Register
I tested to make sure the following worked as designed and All passed
- Enter username (Field must be between 5 and 15 characters long)
- Enter password (Field must be 8 charaters long)
- Confirm password (Field must be equal to password)
- Click on register button (route to
/base
) - Enter existing usename & password (Message saying
username already registered
) - Click on
Already Have An Account Sign
link (route to/sign_in_user
)
Sign-in
I tested to make sure the following worked as designed and All passed
- Enter username
- Enter invalid username (Does not sign-in)
- Enter password
- Enter invalid password (Message saying
Invalid username or password
) - Enter blank password (Message saying
Please fill in this field
) - Click on Not Registered ? link (route to
/register_user
) - Click on sign-in button (route to
/base
)
Sign-out
I tested to make sure the following worked as designed and All passed
- When click on sign out icon
(Message sayingYou have signed out
)
NavBar
I tested to make sure the following worked as designed and All passed
When signed in
you see the following icons
(Home, All Recipes, Add Recipes, My Recipes & Sign Out)
- Click on Add Recipes icon (route to
/<username>/add_recipe
) - Click on My Recipes icon (route to
/<username>/by_my_recipes
)
if there are no recipes by username (Message sayingYou don't have any recipes !
)
Whennot signed
in you see the following icons (Home, All Recipes, Add Recipes, Sign in & Register) - Click on Home icon (route to
/base)
- Click on All Recipes icon (route to
/by_recipes)
- Click on sign in icon (route to
/sign_in_user)
- Click on Register icon (route to
/register_user)
/base
I tested to make sure the following worked as designed and All passed
- When
signed in
you see the following buttons (Add Recipes, All Recipes, My Recipes) - Click on Add Recipes button
(route to/<username>/add_recipe
) - Click on All Recipes button
(route to/by_recipes
) - Click on My recipes button
(route to/<username/by_my_recipes
)
if there are no recipes by username
(Message sayingYou don't have any recipes !
) - Welcome message shows username signed in.
/add_recipe
I tested to make sure the following worked as designed and All passed
The add recipe form has the following input fields:
Cuisine (Select cusine from dropdown)
Categories (Select category(s) from dropdown) to multi-select hold cntl key
Author (Input original recipe author)
Recipe name (Input recipe name)
Recipe description (Input recipe description)
Recipe image (Input recipe url)
Preperation time (Input hh:mm)
Cooking time (Input hh:mm)
Serves (Input servings)
Difficulty (Select difficulty from dropdown
Main ingredient (Input main ingredient)
Allergen (Select allergen(s) from dropdown)to multi-select hold cntl key
Ingredients (Input ingredient) Click Add ingredent
if more ingredients required
Instructions (Input Instructions) Click Add instructions
if more instructions required
-
Click on Add recipe button
(route to'url_for('add_recipe', username=session['username'])'
)
if field not completed then (Message sayingPlease fill in this field
)
when all fields are complete recipe is added to mongodb and
(Message sayingYour recipe has been added
) -
Click on Cancel button
(route to/base
)
/by_recipes
I tested to make sure the following worked as designed and All passed
- Click on allergens button to search dropdown for recipes by not allergen
(route to/not_by_allergen/<allergen_name>
) - Click on Category button to search dropdown for recipes by category
(route to/by_category/<category_name>
) - Click on Difficulty button to search dropdown for recipes by category
(route to/by_difficulty/<difficulty_name>
) - Click on Main ingredient button to search dropdown for recipes by category
(route to/by_main_ingredient/<main_ingredient>
) - Click on Cuisine Card to search for recipes by cuisine
(route to/by_cuisine/<cuisine_name>
) each cuisine card shows number of recipes by cuisine.
/by_my_recipes
I tested to make sure the following worked as designed and All passed
- Click on My Recipes icon to view recipes by username
(route to/<username>/by_my_recipes
)
if there are no recipes by username (Message sayingYou don't have any recipes !
) - Click on recipe card to view recipe details
(route to/view_my_details_recipe/<recipe_id>
) - Click on Delete recipe button to delete recipe
(route to/<username>/delete_recipe/<recipe_id>'
) - Click on Edit recipe button to edit recipe
(route to/<username>/edit_recipe/<recipe_id>'
) you will see edit recipe form.
/edit_recipe
I tested to make sure the following worked as designed and All passed
The edit recipe form has the following fields which are filled in by the recipe to be edited:
Cuisine (cusine from dropdown)
Categories (category(s) from dropdown)
Author (original recipe author)
Recipe name recipe name)
Recipe description (recipe description)
Recipe image (recipe url)
Preperation time (hh:mm)
Cooking time (hh:mm)
Serves (servings)
Difficulty (difficulty from dropdown)
Main ingredient (main ingredient)
Allergen (allergen(s) from dropdown)
Ingredients (ingredient) Click Add ingredent
if more ingredients required.
Instructions (Instructions) Click Add instructions
if more instructions required.
Click on Update recipe button
(route to /update_recipe/<recipe_id>
)
if field not completed then (Message saying Please fill in this field
)
when all fields are complete recipe is updated on mongodb
Click on Cancel button
(route to /base
)
/by_cuisine
I tested to make sure the following worked as designed and All passed
- Click on
recipe card
to view recipe details
(route to/view_details_recipe/<recipe_id>
) - Click on Return to recipes button to return to
(route toby_recipes
) - Click on Vote like button to vote like recipe and increase ratings in recipe card
(route to/vote_like_recipe/<recipe_id>/<cuisine_name>
) - Click on Vote dislike button to vote dislike recipe and decrease ratings in recipe card
(route to/vote_dislike_recipe/<recipe_id>/<cuisine_name>
) - Both vote_like_recipe and vote_dislike_recipe
(redirect toreturn redirect(url_for('vote_if_negative',cuisine_name=cuisine_name))
)
to check if votes are not negative then
(redirect toreturn redirect(url_for('by_cuisine',cuisine_name=cuisine_name))
I created some Bootstrap card grids and there was an issue with different size cards , due to image size, issue was resolved by applying the following changes to the height based on size of device.
See https://stackoverflow.com/a/47698201/1375163
/* Equal-height card images, cf. https://stackoverflow.com/a/47698201/1375163*/
.card-img-top {
/*height: 11vw;*/
object-fit: cover;
}
/* Small devices (landscape phones, 576px and up) */
@media (min-width: 576px) {
.card-img-top {
height: 19vw;
}
}
/* Medium devices (tablets, 768px and up) */
@media (min-width: 768px) {
.card-img-top {
height: 16vw;
}
}
/* Large devices (desktops, 992px and up) */
@media (min-width: 992px) {
.card-img-top {
height: 11vw;
}
}
/* Extra large devices (large desktops, 1200px and up) */
@media (min-width: 992px) {
.card-img-top {
height: 11vw;
}
}
I used open source Bootstrap theme (Creative) by Start Bootstrap so responsive screen issues should be ok.
I inspected via google chrome developer each html page on the following devices
(Responsive, iphone 5/se, iphone 6/7/8/plus, iphone x, ipad, ipad pro) and made any corretions as required.
Listed below are some of the issues I have had building the website , but they have all been resolved.
-
Bootstrap dropdown button issue where all dropdown lists are below the button, but the main ingredient on was above the button , issue was fixed by adding the following to the button
data-display="static"
. -
I am building the view detailed recipe in a card , tried to create a list for the recipe allergens but got the message
TypeError: string indices must be integers
so how do i just showEggs, Milk
in the card ? fixed by coding the following
<span> <strong> Allergens: </strong>
{% for allergen_name in recipes_document_by_recipe.allergen_name %}
{{ allergen_name }}, </span>
{% endfor %}
- I am building the Add recipe html page and would like to be able to click the add new buttons to add more input fields if needed for a recipe. fixed by adding the following javascript code
//Add ingredients form
$('.more-ingredients').click(function () {
addIngredients();
return false; //Stops page jumping back to top
})
function addIngredients() {
var option = `<div class="ing-del">
<input type="text" class="form-control mb-2 mr-2" id="ingredient" name="recipe_ingredient" placeholder="Input ingredient" />
<span class="delete">
<i class="fas fa-times-circle"></i> Del
</span>
</div>`;
$(option).insertBefore('.list-more-ingredients');
}
// Remove ingredients form
$('.ingredients-list').on('click', 'span', function () {
var rem = $(this).closest('div.ing-del');
$(rem).remove();
});
-
I also had an issue with scrollspy
Uncaught TypeError: $(...).scrollspy is not a function
this was due to movingbootstrap.bundle.js
to bottom of script to fix my form issue, i resolved both issues by downloading v4.3.1 of bootstrap. -
I have this code which I want to be able to select multi allergens. I can select multi allergens , but it only seems to print out a single allergen.
changed from this
<!--EDIT ALLERGEN SELECTION-->
<div class="form-group col-md-4">
<label for="edit_allergen_name" class="recipe-label ml-2"> <i class="fas fa-allergies mr-2"></i> Allergen </label>
<select multiple class="custom-select" id="edit_allergen_name" name="allergen_name" required>
<option disabled selected> Select Allergen(s) </option>
{% for allergen_recipe in allergens_recipe: %}
<!-- {% if loop.first %} -->
{% for allergen in allergens_list: %}
{% if allergen == allergen_recipe %}
<option selected value="{{ allergen }}"> {{ allergen }} </option>
{% else %}
<option value="{{ allergen }}"> {{ allergen }} </option>
{% endif %}
{% endfor %}
<!--{% endif %}-->
{% endfor %}
</select>
</div>
to this
<!--EDIT ALLERGEN SELECTION(s)-->
<div class="form-group col-md-4">
<label for="edit_allergen_name" class="recipe-label ml-2"> <i class="fas fa-allergies mr-2"></i> Allergen </label>
<select multiple class="custom-select" id="edit_allergen_name" name="allergen_name" required>
<option disabled selected> Select Allergen(s) </option>
{% for allergen in allergens_list: %}
<option
{% if allergen in allergens_recipe %}
selected
{% endif %}
value="{{ allergen }}"> {{ allergen }}
</option>
{% endfor %}
</select>
</div>
</div>
Testing for this project was implemented manually and the majority of the testing was covered by building and developing flask routes.
Refactoring was implemented while developing this project and I am sure there are things that can be done to streamline code even more, in an additional phase of this project.
Connecting your local repository to your remote repository and pushing the code from your local repository to your remote repository.
Create new remote repository
- Sign into GitHub
- Click on new button
- Enter Repository Name
data-centric-dev-website
- Enter Description (optional)
My Data Centric Development Project Repository
- Click on Create repository button
From Clould9
from local repository enter the following command which can be cut and pasted from the website as below:
…or push an existing repository from the command line
`git remote add origin https://github.com/bennettpe/data-centric-dev-website.git`
`git push -u origin master`
After entering these commands you will be asked for your github Username and Password and your local repository will be pushed to your remote repository
bennettpe:~/workspace (master) $ git remote add origin https://github.com/bennettpe/data-centric-dev-website.git
bennettpe:~/workspace (master) $ git push -u origin master
Username for 'https://github.com': bennettpe
Password for 'https://[email protected]':
Counting objects: 2200, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (2183/2183), done.
Writing objects: 100% (2200/2200), 15.36 MiB | 586.00 KiB/s, done.
Total 2200 (delta 453), reused 0 (delta 0)
remote: Resolving deltas: 100% (453/453), done.
To https://github.com/bennettpe/data-centric-dev-website.git
- [new branch] master -> master
Branch master set up to track remote branch master from origin.
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 app.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", "5149fde2f2f15a6f77dddf0f319b20c6")
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 app.py > Procfile
-
The Procfile file contains
web: python app.py
which tells Heroku to start a process called web and to runpython app.py
when it starts. -
Type in the following command in the terminanl 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:
-
Type in the following command in the terminal window which adds all project files:
$ git add .
-
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"
-
Type in the following command into the terminal window to run the heroku login command
$ heroku login
-
Type into the terminal window your email address and password.
-
Type into the terminl window
$ git remote -v
Heroku references have already been added. -
Now you are logged into Heroku you need to create a new heroku app by typing the following command
$ heroku apps:create data-centric-dev-project
,once created it will also give us a git address as well. -
Before we push our app to Heroku we need to set our enviroment variables
https://dashboard.heroku.comm/apps
In Heroku (Config vars)
- Refresh the Heroku dashboard and you should see your new heroku app
my-data-centric-dev-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 enter"5149fde2f2f15a6f77dddf0f319b20c6"
and then click on Add Button.
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.
In Heroku (Open app)
- Click Open app
- Select new tab, [my data centric development project](https:// data-centric-dev-project.herokuapp.com/)
- Background image for Top of website taken from pixabay
- All Images for Cuisine and Recipe taken from bbc goodfood
- 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)
- Message Flashing with categories was used as framework
Pretty Printed https://youtu.be/lcVdZtVvnnk - 14 Allergens list from food standards agency
https://www.food.gov.uk/sites/default/files/media/document/top-allergy-types.pdf - Bootstrap card grid image sizing issue
https://stackoverflow.com/a/47698201/1375163