Football memories is a website that allows users to add/edit/delete/view football memories for given tournaments.
It also allows users to rate and comment on football memories
- Project Overview
- UX
- Features
- Technologies Used
- Testing
- APIs
- Deployment
- Credits
- Content
- Media
- Acknowledgements
Table of contents generated with markdown-toc
- This project is a website that allows users to add/edit/delete football memories for given tournament for submission as milestone project 3 as part of the Code Institute - Diploma in Software Development (Full stack) course.
- The repository on GitHub that contains the website source code and assets is available at the following url: Code Repository
- The website was built with a responsive look and feel for desktop, tablet and mobile devices
The primary goal of the website from the site owners perspective is as follows:
- To create/edit/delete tournaments so users can add a memory to a tournament(name and image)
- To allow users add their football memories(name, image, tournament, description, date, stadium)
- To allow users modify their football memories(name, image, tournament, description, date, stadium)
- To allow users delete their football memories
- To allow users view their memories and other users memories
- To allow users comment on a memory
- To allow users rate a memory with a score from 1-5
- To view statistics on the usage of the site
The primary goal of the website from a site users perspective is as follows:
- To allow users add their football memories(name, image, tournament, description, date, stadium)
- To allow users modify their football memories(name, image, tournament, description, date, stadium)
- To allow users delete their football memories
- To view their memories and other users memories
- To comment on a memory and view comments
- To rate a memory with a score from 1-5 and view ratings of a memory
I have structured the website into 19 pages, each with clear, concise structure, information and purpose. I use the Bootstrap grid system throughout, which gave a consistent structure and responsive design "out of the box"
- Home/Landing Page: This is the landing page, and the first page the user encounters when they access the site, before they log in/register
- Register: This page allows the user to register an account to use the site
- Login: This page allows the user to login to the site
- Terms and Conditions: This page displays the sites terms and conditions
- Privacy Policy: This page displays the sites' privacy policy
- Memories: This is the first page the user sees when the login/register, it displays all the football memories that have been added
- Add Memory: This page allows the user to add a football memory
- Edit Memory: This page allows a user to edit a memory they have created
- Delete Memory: This button allows a user to delete a memory they have created
- Profile: This page displays user information and allows the user to update their profile
- Dashboard: This page displays statistics about the number of users, tournaments memories, comments
- Tournaments: This page displays the tournaments that have been created by the admin user. An admin user can add/edit or delete a tournament
- Add Tournament: This page allows an admin user to add a football tournament
- Edit Tournament: This page allows an admin user to edit a football tournament
- Delete Tournament: This button allows an admin user to edit a football tournament as long as there are no memories associated and a minimum of one tournament in the tournament collection
- Logout: This link allows the user to logout of the site
- Newsletter: The user can enter their email address in the site footer and subscribe to a mailing list
- 404: The 404 error page is displayed if the user enters an incorrect url when accessing the site.
- 400, 401, 405 and 500: The error page is displayed if the user encounters an error on the site
- My project is built using a Blueprints structure
- A blueprint in Flask is an object to structure a Flask application into subsets. This helped in organizing code and separating functionality.
- I found the following video and links invaluable to structure my project accordingly
- The project is structured as follows
- administration: Contains a flask route for administration code, for example a dashboard
- authentication: Contains a flask route for authentication for example login, register
- errors: Contains a flask route for error pages for example 404
- memories: Contains a flask route for memory code, adding, editing etc
- static
- css (Project style css)
- images (Project and readme images)
- js (Project javascript structured into individual files)
- templates: Html templates to match the routes for Administration, Authentication, Errors, Memories, Tournaments and a base.html file
- tournaments: Contains a flask route for tournament code, adding, editing etc
- util: Utilities, common code for example aws code for writing to an aws s3 bucket
- An app.py that setups, creates and runs the application
- A local env.py(that is not committed to source control) - This ensures passwords and security-sensitive information are stored in environment variables or in files that are in .gitignore, and are never committed to the repository
- The website is a data-centric one with html, javascript, css used with the bootstrap framework as a frontend
- The backend consists of Python, flask and jinja templates with a database of a mongodb open-source document-oriented database
The first step in the database design was to create a conceptual data model. The helped me understand the design at a conceptual level while enabling me to understand the required collections in the database
From the conceptual database model I created the physical database model. This model contains all fields stored in the database collections with their data type and mimics the structure of what is actually stored in the mongo database(mongodb)
Note: The lines/links in the diagram denote the relationship in the python code between the different collection fields and not foreign keys, for example
when a memory is created in the memories' collection, it also stores the tournament name from the tournament's collection.
- One production database(football_memories_prod) was created to store site information, it contains five collections described below
- users - to store registered user information
- tournaments - to store tournament information added by an admin user
- memories - to store memory information added by an admin/regular user
- comments - to store comment information for a memory added by an admin/regular user
- ratings - to store rating information for a memory added by an admin/regular user
- I created a model.py file in football_memories/models to show the structure of the database in Python code.
- For the purpose of this project I created the database and five collections manually in the mongodb cloud instance online
- I did not create a script to load in this model as I used the mongodb website and configured the database and collections there
- So the file is there as an example to show I have investigated how models work in Python in a flask application
- The users' collection is used to store user information when they register.
- The fields stored in the collection are users username(String), password(String), first_name(String), last_name(String), favourite_team(String) and country(String) with a unique identifier(primary key) , "_id"(Object Id)
- The users' password is encrypted using a generate_password_hash from a werkzeug.security Python library.
- The tournaments that memories are added to are added by an admin user.
- The fields stored in the collection are the tournament name(String) and tournament image(String) with a unique identifier(primary key) automatically assigned by the mongodb, "_id"(Object Id) primary key
- Memories are added by regular and admin users
- The fields stored in the collection are the memory_image(String), tournament name(String), memory name(String), memory_description(String), memory_date(String), memory_stadium(String), memory_view_count(Int32), memory_created_by(username) with a unique identifier(primary key) automatically assigned by the mongodb, "_id"(Object Id)
- When a user adds a memory, it stores the tournament name(from the tournaments added in the tournament table) and a memory_created_by(take from the User tables username field of the user who added the memory) to create a link between the two collections
- The memory_name and memory_description have search indexes setup for searching functionality
- Comments are added to a memory by a regular or admin user
- The fields stored in the collection are the memory_id(String), comment_text(String), comment_date(String), comment_created_by(String) with a unique identifier(primary key) automatically assigned by the mongodb, "_id"(Object Id)
- When a user adds a comment, it stores the memory_id(from the memories table) in the collection and the comment_created_by field, storing the username(from the User table) who added the comment to create a link between the two collections
- Ratings are added to a memory by a regular or admin user
- The fields stored in the collection are the rating value(Int32), the rating_created_by(String) with a unique identifier(primary key) automatically assigned by the mongodb, "_id"(Object Id)
- When a user adds a rating, the rating_created_by field populated with the users' username(from the User table) to create a link between the two collections
While mongodb stores the majority of the users' data in the database, any images added by a regular user or admin user when adding a new tournament or memory is stored in an Amazon Web services(AWS) S3(storage) bucket. I made this choice for performance reasons(https://aws.amazon.com/s3/faqs/) and to challenge myself to learn how to integrate the site with AWS. Here are the steps I took for the integration
- I created an account with AWS, and created an S3 bucket named "ci-ms3-football-memories"
- I created a user in AWS IAM, and gave the user the AmazonS3FullAccess permission
- I then gave the bucket policy the necessary permissions to allow my application to access the S3 bucket
- I imported the Boto3 python library (https://boto3.amazonaws.com/) in the util.py file I made a design decision to have an util.py in an util flask route python file that would be used to store common code that could be used by multiple routes
- The relevant s3 variables for the bucket name, url and access/secret keys are defined at the top of the util.py file
s3_bucket_name = "ci-ms3-football-memories" s3_bucket_url = "https://ci-ms3-football-memories.s3.eu-west-1.amazonaws.com/" client = boto3.client('s3', aws_access_key_id=os.environ.get("AWS_ACCESS_KEY_ID"), aws_secret_access_key=os.environ.get("AWS_SECRET_ACCESS_KEY"))
- A single function was written named storeImageAWSS3Bucket that takes one parameter, the filename to store
- This single function is used by the tournament and memories routes to store the images in the S3 bucket
- This function stores a file in an AWS S3 bucket using boto3. The filename is in the form timestamp + name of file added by the user. The timestamp ensures uniqueness for every file added to the s3 bucket allowing users to use the same filename if desired.
image_file = secure_filename(image.filename) image_to_upload = timestamp + image_file
- The boto3 put_object method is used to store the image taking two parameters, the file name and actual file
s3.Bucket(s3_bucket_name).put_object(Key=image_to_upload, Body=image)
- An image url is returned, and it is the image url that is stored in the mongodb for the relevant tournament or memory, described below in the two screenshots, field
names memory_image and tournament_image in the memory and tournament collections
image_url = s3_bucket_url + image_to_upload
There is overlap in terms of user stories for the two types of users, and they are described below
The user stories for the website user "regular user" (a potential or existing customer) are described as follows:
- User Story 1.1: As a regular user the navigation bar is displayed with a logo on all pages for easy navigation, with a burger menu on mobile devices
- User Story 1.2: As a regular user the navigation item selected is highlighted
- User Story 1.3: As a regular user, when logged out, the home/landing page is the default page and there are three options with a logo, Home, Login, Register displayed
- User Story 1.4: As a regular user, when logged in, the memories page is the default page and there are six options with a logo: Memories, Add Memory, Tournaments, Profile, Dashboard, Logout
- User Story 1.5: As a regular user if I encounter a route that does not exist I am navigated to a 404 error page
- User Story 2.1: As a regular user I can view the footers social icons(Twitter, facebook, instagram, pinterest, snapchat) and the relevant website opens in a new tab when clicked
- User Story 2.2: As a regular user I can view the websites terms and condition page by clicking on the link in the footer
- User Story 2.3: As a regular user I can view the websites' privacy policy page by clicking on the link in the footer
- User Story 2.4: As a regular user I can sign up to the football memories newsletter by entering my email and clicking SignUp. The email address entered will receive an email
- User Story 3.1: As a regular user I can view a hero image with login and register buttons on the home/landing page
- User Story 3.2: As a regular user I can view the last three memories added on the website, with memory name, memory image and tournament
- User Story 3.3: As a regular user if I encounter a route that does not exist I am navigated to a 404 error page
- User Story 3.4: As a regular user if I encounter an error with the application starting up I am navigated to a 500 error page
- User Story 3.5: As a regular user if I encounter an error when using the application(adding a memory or tournament for example), a message is displayed
- User Story 4.1: As a regular user I can register for an account by providing my username, password, confirm password, first name, last name, favourite team and country, and I will be brought to the memories page. All fields must be provided
- User Story 4.2: As a regular user my username must be a minimum of 6 characters, and contain at least one lowercase letter, with no special characters
- User Story 4.3: As a regular user my password must be a minimum of 6 characters and contain at least one number, one lowercase and one uppercase letter, with no special characters
- User Story 4.4: As a regular user my password must match my confirm password
- User Story 4.5: As a regular user I can log in to my account by providing my username and password and clicking Login and I will be navigated to the memories page. A username and password must be provided. If the username and/or password entered is incorrectly a relevant message will be displayed
- User Story 4.6: As a regular user, when I am logged into the site, and I click Logout I am successfully logged out of the site, and brought to the home/landing page, and the navigation bar is updated with three options with a logo, Home, Login, Register
- User Story 4.7: As a regular user, when I am logged into the site, and I click the back button I am automatically redirected to the home/landing page, and the navigation bar is updated with three options with a logo, Home, Login, Register
- User Story 5.1: Add Memory - As a regular user I can add a memory by selecting a tournament, uploading a memory image, entering a memory name, description, date and stadium. All fields are mandatory
- User Story 5.2: Add Memory - As a regular user the memory image I upload must be png or jpg format
- User Story 5.3: Edit Memory - As a regular user I can edit a memory by uploading a memory image, updating the memory name, description, date and stadium. All fields are mandatory
- User Story 5.4: Delete Memory - As a regular user I can delete a memory I created by confirming I want to delete
- User Story 5.6: View Memory - As a regular user I can view a memory by clicking on a memory
- User Story 5.7: View Memory - As a regular user I can view the memory image, stadium in a Google map, average rating, view account, memory name, description, tournament name, date, view count, uploaded by information and all comments for the memory, paginated if there are more than 3 comments
- User Story 5.8: View Memory - As a regular user the view count increases when the user clicks on a memory or refreshes the memory page
- User Story 5.9: View Memory - As a regular user I can add a comment to a memory
- User Story 5.10: View Memory - As a regular user I can add a comment to a rating to a memory, and the average rating is updated accordingly
- User Story 5.11: My Memories- As a regular user I can filter on memories I have created, and the page will be paginated if there are more than 3 memories
- User Story 5.12: All Memories - As a regular user I can filter on memories I have created, and the page will be paginated if there are more than 3 memories
- User Story 5.13: Search - As a regular user I can search on text for the memory name and memory description, and the memories page will be updated with those filtered memories
- User Story 6.1: As a regular user I can view a list of tournaments with the tournament name and tournament image displayed, and pagination is displayed if there are more than 3 tournaments
- User Story 7.1: As a regular user I can view a dashboard to see the number of users, number of tournaments, number of memories, number of ratings and number of comments added on the site
- User Story 8.1: As a regular user I can view my profile details: Username, First Name, Last Name, Favourite Team and Country. The country is selected from a dropdown of countries
- User Story 8.2: As a regular user I can update my profile password, but the confirm password entered must match with the password. The password must be 8-20 characters long, contain letters and numbers, and must not contain spaces, special characters, or emoji.
- User Story 8.3: As a regular user I can update my profile details: First Name, Last Name, Favourite Team and Country
- User Story 8.4: As a regular user the following fields are mandatory: Username, First Name, Last Name, Favourite Team and Country
- User Story 8.5: As a regular user I can delete my account. This will delete any memories I have added(including their associated comments and ratings) and will also delete any comments or ratings the regular user has added on others users memories. The user will be asked to confirm the delete account action, and will be brought to the homepage after their account is successfully deleted.
The user stories for the website owner(admin user) are described as follows: There is a lot of overlap between the two user types, the admin user however has more administrative rights throughout
- User Story 1.1: As an admin user the navigation bar is displayed with a logo on all pages for easy navigation, with a burger menu on mobile devices
- User Story 1.2: As an admin user the navigation item selected is highlighted
- User Story 1.3: As an admin user, when logged out, the home/landing page is the default page and there are three options with a logo, Home, Login, Register displayed
- User Story 1.4: As an admin user, when logged in, the memories page is the default page and there are six options with a logo: Memories, Add Memory, Tournaments, Profile, Dashboard, Logout
- User Story 2.1: As an admin user I can view the footers social icons(Twitter, facebook, instagram, pinterest, snapchat) and the relevant website opens in a new tab when clicked
- User Story 2.2: As an admin user I can view the websites terms and condition page by clicking on the link in the footer
- User Story 2.3: As an admin user I can view the websites' privacy policy page by clicking on the link in the footer
- User Story 2.4: As an admin user I can sign up to the football memories newsletter by entering my email and clicking SignUp. The email address entered will receive an email
- User Story 3.1: As an admin user I can view a hero image with login and register buttons on the home/landing page
- User Story 3.2: As an admin user I can view the last three memories added on the website, with memory name, memory image and tournament
- User Story 3.3: As an admin user if I encounter a route that does not exist I am navigated to a 404 error page
- User Story 3.4: As an admin user if I encounter an error with the application starting up I am navigated to a 500 error page
- User Story 3.5: As an admin user if I encounter an error when using the application(adding a memory or tournament for example), a message is displayed
- User Story 4.5: As an admin user I can log in to my account by providing my username and password and clicking Login and I will be navigated to the my memories page. A username and password must be provided. If the username and/or password entered is incorrectly a relevant message will be displayed
- User Story 4.6: As an admin user, when I am logged into the site, and I click Logout I am successfully logged out of the site, and brought to the home/landing page, and the navigation bar is updated with three options with a logo, Home, Login, Register
- User Story 4.7: As an admin user, when I am logged into the site, and I click the back button I am automatically redirected to the home/landing page, and the navigation bar is updated with three options with a logo, Home, Login, Register
- User Story 5.1: Add Memory - As an admin user I can add a memory by selecting a tournament, uploading a memory image, entering a memory name, description, date and stadium. All fields are mandatory
- User Story 5.2: Add Memory - As an admin user the memory image I upload must be png or jpg format
- User Story 5.3: Edit Memory - As an admin user I can edit a memory by uploading a memory image, updating the memory name, description, date and stadium. All fields are mandatory
- User Story 5.5: Delete Memory - As an admin user I can delete a memory, created by any user or a memory I have created
- User Story 5.6: View Memory - As an admin user I can view a memory by clicking on a memory
- User Story 5.7: View Memory - As an admin user I can view the memory image, stadium in a Google map, average rating, view account, memory name, description, tournament name, date, view count, uploaded by information and all comments for the memory, paginated if there are more than 3 comments
- User Story 5.8: View Memory - As an admin user the view count increases when the user clicks on a memory or refreshes the memory page
- User Story 5.9: View Memory - As an admin user I can add a comment to a memory
- User Story 5.10: View Memory - As an admin user I can add a comment to a rating to a memory, and the average rating is updated accordingly
- User Story 5.11: My Memories- As an admin use user I can filter on memories I have created, and the page will be paginated if there are more than 3 memories
- User Story 5.12: All Memories - As an admin use user I can filter on memories I have created, and the page will be paginated if there are more than 3 memories
- User Story 5.13: Search - As an admin user I can search on text for the memory name and memory description, and the memories page will be updated with those filtered memories
- User Story 6.2: As an admin user I can view list of tournaments with the tournament name and tournament image displayed, and pagination is displayed if there are more than 3 tournaments. On the tournaments page I can also view an Add Tournament, Edit Tournament and Delete Tournament buttons
- User Story 6.3: As an admin user I can add a new tournament with a tournament name and tournament image. Both fields are mandatory and a message is displayed accordingly. The tournament information is added in the mongo database and the tournament image is stored in an AWS S3 bucket
- User Story 6.4: As an admin user I can edit an existing tournament with a tournament name and tournament image. Both fields are mandatory and a message is displayed accordingly. The tournament information is updated in the mongo database and the tournament image is stored in an AWS S3 bucket
- User Story 6.5: As an admin user I can delete an existing tournament, once I have confirmed that it is ok to delete the tournament. The tournament information is deleted from the mongo database
- User Story 6.6: As an admin user if a tournament has memories associated with it, the tournament cannot be deleted and a message is displayed
- User Story 6.7: As an admin user if there is only one tournament in website, it cannot be deleted, a minimum of one tournament is required
- User Story 7.1: As an admin user I can view a dashboard to see the number of users, number of tournaments, number of memories, number of ratings and number of comments added on the site
- User Story 8.1: As an admin user I can view my profile details: Username, First Name, Last Name, Favourite Team and Country. The country is selected from a dropdown of countries
- User Story 8.2: As an admin user I can update my profile password, but the confirm password entered must match with the password. The password must be 8-20 characters long, contain letters and numbers, and must not contain spaces, special characters, or emoji.
- User Story 8.3: As an admin user I can update my profile details: First Name, Last Name, Favourite Team and Country
- User Story 8.4: As an admin user the following fields are mandatory: Username, First Name, Last Name, Favourite Team and Country
Each wireframe image below contains three sub images, one for desktop, tablet and mobile
Page | Wireframe |
---|---|
index | Desktop/Tablet/Mobile |
register | Desktop/Tablet/Mobile |
login | Desktop/Tablet/Mobile |
memories | Desktop/Tablet/Mobile |
memory | Desktop/Tablet/Mobile |
add_memory/edit_memory | Desktop/Tablet/Mobile |
delete_memory | Desktop/Tablet/Mobile |
tournaments | Desktop/Tablet/Mobile |
add_tournament/edit_tournament | Desktop/Tablet/Mobile |
delete_tournament | Desktop/Tablet/Mobile |
profile | Desktop/Tablet/Mobile |
dashboard | Desktop/Tablet/Mobile |
terms_and_conditions | Desktop/Tablet/Mobile |
privacy_policy | Desktop/Tablet/Mobile |
I have gone for a simple and minimal design for the website, with predominately green, black and white font colours over a large hero image on all pages There are five colours in the color palette
- 264653 - Dark green colour for some button and text colours
- 006400 - Light green colour for some button and text colours
- 000000 - Black color for some text
- F8F9FA - Light grey colour for the header and footer and panel backgrounds
- DC3545 - Red colour for some buttons, cancel and delete buttons
I feel the colours complement each other very well, and I choose those colours after testing a number of palettes while making sure the colour palette met accessibility standards.
The Poppins font is the main font used throughout the whole website with Sans Serif as the fallback font in case for any reason the Poppins font cannot be imported into the website correctly. This font is from the Google fonts library.
The website has seven distinct features, and they are described below
-
This is the navigation bar of the website, and is displayed on all pages. The navigation bar is a bootstrap component, and is a responsive component. It becomes a burger menu on tablet and mobile devices.
-
When the user is not logged in, there are three options with a logo, Home, Login, Register
-
When the user is logged in, there are six options with a logo: Memories, Add Memory, Tournaments, Profile, Dashboard, Logout
-
This ensures any functionality requiring log-in is available only to logged-in users
-
Clicking on the Logout button logs the user out of the site, and their session is ended. If they click back they are automatically re-sent back to the home/landing page
-
The following code is a check on the relevant functions in every flask route on the website
if 'user' not in session:
return redirect(url_for("administration.home"))
- User Story 1.1: As an admin/regular user the navigation bar is displayed with a logo on all pages for easy navigation, with a burger menu on mobile devices
- User Story 1.2: As an admin/regular user the navigation item selected is highlighted
- User Story 1.3: As an admin/regular user, when logged out, the home/landing page is the default page and there are three options with a logo, Home, Login, Register displayed
- User Story 1.4: As an admin/regular user, when logged in, the memories page is the default page and there are six options with a logo: Memories, Add Memory, Tournaments, Profile, Dashboard, Logout
The footer of the website is displayed on all pages. It consists of three main sections
- Logo with text and social media links
- Terms and Conditions and Privacy Policy links
- Newsletter sign-up
The terms and conditions and privacy policy contain the relevant information, it is test data for the purpose of having these pages on the website When the user enters their email address and clicks Sign-up the div is updated with a message
- User Story 2.1: As an admin/regular user I can view the footers social icons(Twitter, facebook, instagram, pinterest, snapchat) and the relevant website opens in a new tab when clicked
- User Story 2.2: As an admin/regular user I can view the websites terms and condition page by clicking on the link in the footer
- User Story 2.3: As an admin/regular user I can view the websites' privacy policy page by clicking on the link in the footer
- User Story 2.4: As an admin/regular user I can sign up to the football memories newsletter by entering my email and clicking SignUp. The email address entered will receive an email
The landing/home page is displayed when the user first accessing the site, and when they log out. It displays a hero image with Login/Register buttons
Below the hero image are the last three memories added by users. The following code is used to query the mongodb in the administration route python file
three_latest_memories = list(mongo.db.memories.find().
sort("_id", -1).limit(3))
- User Story 3.1: As a regular user/admin user I can view a hero image with login and register buttons on the home/landing page
- User Story 3.2: As a regular user/admin user I can view the last three memories added on the website, with memory name, memory image and tournament
- User Story 3.3: As an admin/regular user if I encounter a route that does not exist I am navigated to a 404 error page
- User Story 3.4: As an admin/regular user if I encounter an error with the application starting up I am navigated to a 500 error page
- User Story 3.5: As an admin/regular user if I encounter an error when using the application(adding a memory or tournament for example), a message is displayed
- The user can register for an account on the website by entering their username, password, confirming their password, first name, last name, favourite team, country.
- All fields are mandatory and a relevant message will be displayed and the username must be unique otherwise a message will be displayed to the user
- The username must be a minimum of 6 characters and contain at least one lowercase letter, with no special characters
- The password must be a minimum of 6 characters and contain at least one number, one lowercase and one uppercase letter, with no special characters
- The password must match the confirm password
- The country is selected from a dropdown list of countries
- Note: I have limited the functionality so that only regular users and not admin users can register on the site
- The user can log in into their account by clicking on the Login button on the landing page or clicking the Login link in the navigation bar. They must enter a valid username and password otherwise a relevant message will be displayed.
- A 404 page is displayed if the page the user accesses does not exist
- When the user registers or logs in to the site successfully, they are brought to the memories page
and a welcome message is displayed, for example
flash("Registration Successful!")
- User Story 4.1: As a regular user I can register for an account by providing my username, password, confirm password, first name, last name, favourite team and country, and I will be navigated to the memories page. All fields must be provided
- User Story 4.2: As a regular user my username must be a minimum of 6 characters, and contain at least one lowercase letter, with no special characters
- User Story 4.3: As a regular user my password must be a minimum of 6 characters and contain at least one number, one lowercase and one uppercase letter, with no special characters
- User Story 4.4: As a regular user my password must match my confirm password
- User Story 4.5: As a regular user/admin user I can log in to my account by providing my username and password and clicking Login and I will be navigated to the memories page. A username and password must be provided. If the username and/or password entered is incorrectly a relevant message will be displayed
- User Story 4.6: As an admin/regular user, when I am logged into the site, and I click Logout I am successfully logged out of the site, and brought to the home/landing page, and the navigation bar is updated with three options with a logo, Home, Login, Register
- User Story 4.7: As an admin/regular user, when I am logged into the site, and I click the back button I am automatically redirected to the home/landing page, and the navigation bar is updated with three options with a logo, Home, Login, Register
- A user can add a memory by clicking on the Add Memory link and providing the following information
- Tournament Name, a dropdown appears with a list of tournament
- Memory Image, an image upload that the user can upload an image. The file types allowed are jpg and png, and a message is displayed if the user tried to add a memory with an image type not matching jpg or png
- Memory Name, a text field, where the user can enter a memory name
- Memory Description, a textarea, where the user can enter a memory description, date(calendar date in the form dd/mm/yyyy) and stadium
- All fields are mandatory Clicking cancel will bring the user back to the Memories page, clicking Add Memory will save the changes and create the memory
- A user can edit an existing memory by clicking on the "Edit Memory" button on a memory(My memories) and providing the following information
- Tournament Name, the selected tournament is displayed
- Memory Image, an image upload that the user can upload an image. The file types allowed are jpg and png, and a message is displayed if the user tried to add a memory with an image type not matching jpg or png. For security purposes a new image must be uploaded
- Memory Name, a text field, where the user can update a memory name
- Memory Description, a textarea, where the user can update a memory description, date(calendar date in the form dd/mm/yyyy) and stadium
- All fields are mandatory
- A user can only edit memories they have created and not other users memories
- Clicking cancel will bring the user back to the Memories page, clicking Save Changes will save the changes and update the memory
- A user can delete a memory they have created, and admin user can delete any memory
- A regular user can only create memories they have created
- A user can view all memories or filter on memories they have created on the Memories page. When the login/register they are navigated to the "All Memories" page, but when they add/edit/delete a memory, they are navigated to their memories page. Memories are sorted by the date they are added with the newest memories displayed first
- Pagination will be displayed if there are more than 3 memories
- A user can search memories based on text on the memory name and description, the user can also reset the search. The search is not case-sensitive
- A user can click on a memory in the memories page to view detailed information about the memory
- They can view a larger image of the memory
- They can view a Google map of the stadium that was entered for the memory
- They can view the memory name, memory description, tournament name, stadium, date
- They can view the view count of the image, and this value increments when the memory is viewed
- They can view the average rating for a memory(total of ratings/number of ratings)
- They can submit a rating for the memory, with a value of 1-5
- They can view all comments for a memory, and the page is paginated if there are more than 3 comments
- They can add a comment, and the comment is displayed with the comment, added by and date
- In all user stories, the user will not be asked for information they have already supplied, for example when the user is adding a memory, they will not be asked for their username
- When the user add/edit/deletes a memory, adds a comment, adds a rating successfully, a relevant message
is displayed, for example
flash("Rating Successfully Added")
- User Story 5.1: Add Memory - As a regular user/admin user I can add a memory by selecting a tournament, uploading a memory image, entering a memory name, description, date and stadium. All fields are mandatory
- User Story 5.2: Add Memory - As a regular user/admin user the memory image I upload must be png or jpg format
- User Story 5.2: Edit Memory - As a regular user/admin user I can edit a memory by uploading a memory image, updating the memory name, description, date and stadium. All fields are mandatory
- User Story 5.3: Delete Memory - As a regular user I can delete a memory I created by confirming I want to delete
- User Story 5.3: Delete Memory - As an admin user I can delete a memory, created by any user
- User Story 5.4: View Memory - As a regular user/admin user I can view a memory by clicking on a memory
- User Story 5.5: View Memory - As a regular user/admin user I can view the memory image, stadium in a Google map, average rating, view account, memory name, description, tournament name, date, view count, uploaded by information and all comments for the memory, paginated if there are more than 3 comments
- User Story 5.5: View Memory - As a regular user/admin user the view count increases when the user clicks on a memory or refreshes the memory page
- User Story 5.6: View Memory - As a regular user/admin user I can add a comment to a memory
- User Story 5.7: View Memory - As a regular user/admin user I can add a comment to a rating to a memory, and the average rating is updated accordingly
- User Story 5.8: My Memories- As a regular user/admin user I can filter on memories I have created, and the page will be paginated if there are more than 3 memories
- User Story 5.9: All Memories - As a regular user/admin user I can filter on memories I have created, and the page will be paginated if there are more than 3 memories
- User Story 5.10: Search - As a regular user/admin I can search on text for the memory name and memory description, and the memories page will be updated with those filtered memories
-
A regular user can view the tournaments they can add memories to. Three tournaments are displays per page(tournament name, tournament image), and pagination is displayed if there are more than three tournaments in the mongodb database. The tournaments are sorted and displayed by date, most recently added first
-
Adding a tournament requires a tournament name and tournament image field to be filled in. The file types allowed are jpg and png, and a message is displayed if the admin user tries to add a tournament with an image type not matching jpg or png
-
Editing a tournament requires a tournament name and tournament image field to be filled in. The file types allowed are jpg and png, and a message is displayed if the admin user tries to add a tournament with an image type not matching jpg or png
-
In all user stories, the user will not be asked for information they have already supplied, for example when the user is adding a tournament, they will not be asked for their username
-
When an admin user add/edits/deletes a tournament a relevant message is displayed, for example
flash("Tournament Successfully Added")
-
Only an admin user can add/edit/delete a tournament, a regular user can not
- User Story 6.1: As a regular user/admin user I can view a list of tournaments created with the tournament name and tournament image displayed
- User Story 6.2: As a regular user/admin user the list of tournaments is displayed with three per page, and pagination is displayed if there are more than 3 tournaments
- User Story 6.3: As an admin user I can add a new tournament with a tournament name and tournament image. Both fields are mandatory and a message is displayed accordingly. The tournament information is added in the mongo database and the tournament image is stored in an AWS S3 bucket
- User Story 6.4: As an admin user I can edit an existing tournament with a tournament name and tournament image. Both fields are mandatory and a message is displayed accordingly. The tournament information is updated in the mongo database and the tournament image is stored in an AWS S3 bucket
- User Story 6.5: As an admin user I can delete an existing tournament, once I have confirmed that it is ok to delete the tournament. The tournament information is deleted from the mongo database
- User Story 6.6: As an admin user if a tournament has memories associated with it, the tournament cannot be deleted and a message is displayed
- User Story 6.7: As an admin user if there is only one tournament in website, it cannot be deleted, a minimum of one tournament is required
The dashboard page displays the results of 5 queries against the mongo db for the number of users, number of tournaments, number of memories, number of ratings and number of comments added on the site
number_of_users = mongo.db.users.count()
number_of_tournaments = mongo.db.tournaments.count()
number_of_memories = mongo.db.memories.count()
number_of_comments = mongo.db.comments.count()
number_of_ratings = mongo.db.ratings.count()
- User Story 7.1: As a regular user/admin user I can view a dashboard to see the number of users, number of tournaments, number of memories, number of ratings and number of comments added on the site
- A user can view or edit their profile details. Their username is displayed, but it is an un-editable field. When the user clicks save changes they are brought back to the Profile page with the relevant updates made
- A user cannot update their username, the field is read only on the profile page
- When the user selects the profile page, all their information(username, first name, last name, favourite team, country) is displayed, the user will not be asked for information they have already supplied
- However, the user is asked to re-enter their password, this is for security purposes and the fact the user's password is encrypted in the database
- All fields are mandatory, similar to the registration page
- User Story 8.1: As a regular user/admin user I can view my profile details: Username, First Name, Last Name, Favourite Team and Country. The country is selected from a dropdown of countries
- User Story 8.2: As a regular user/admin user I can update my profile password, but the confirm password entered must match with the password. The password must be 8-20 characters long, contain letters and numbers, and must not contain spaces, special characters, or emoji.
- User Story 8.3: As a regular user/admin user I can update my profile details: First Name, Last Name, Favourite Team and Country
- User Story 8.4: As a regular user/admin user the following fields are mandatory: Username, First Name, Last Name, Favourite Team and Country
- User Story 8.5: As a regular user I can delete my account. This will delete any memories I have added(including their associated comments and ratings) and will also delete any comments or ratings the regular user has added on others users memories. The user will be asked to confirm the delete account action, and will be brought to the homepage after their account is successfully deleted.
- I am content with what was implemented. The site is a feature rich site using a number of linked namespaces in a mongodb collection.
- However, here are some additional "nice to have" features that could be added to the site
Number | Feature |
---|---|
1 | Social sharing of a memory on facebook, twitter |
2 | The image type(jpg, png) is read and the memory displays a different helper image based on it |
3 | Tags functionality and search by tags |
4 | Enhance reporting/dashboard capabilities, and use a 3pp graph library |
5 | User must verify their email address when registering, or 2 factor authentication is implemented |
6 | Delete image from aws s3 bucket, when a tournament or memory is deleted. I did attempt to do this but came across permission issues. Despite making numerous changes to the bucket policy I was unable to get the code working. I don't see this as a major issue as there is a lot of space available in the S3 bucket |
7 | Continue to make further changes to the blueprint structure, and move all mongodb database related code to its own route |
- HTML (https://en.wikipedia.org/wiki/HTML)
- The project uses html to build the relevant pages
- CSS (https://en.wikipedia.org/wiki/CSS)
- The project uses CSS to style the relevant pages
- Javascript (https://www.javascript.com/)
- Javascript was used for all scripting on the site
- Python v3.9 (https://www.python.org/)
- Python was used for server side coding on the project, a number of libraries were also used:
- boto, boto3, botocore, click, dnspython, Flask, flask-paginate, Flask-PyMongo, itsdangerous, Jinja2, jmespath, MarkupSafe, pymongo, s3transfer,Werkzeug
- Python was used for server side coding on the project, a number of libraries were also used:
- Jinja (https://jinja.palletsprojects.com/en/3.0.x/)
- Jinja is a templating engine for Python that is used throughout the project
- Bootstrap 5.0 (https://getbootstrap.com/docs/5.0)
- The project uses the bootstrap library for some UI components in the website
- Gitpod (https://gitpod.io/)
- Gitpod was used as an IDE for the project
- Github (https://github.com/)
- GitHub was used to store the project code in a repository
- Google Fonts (https://fonts.google.com/)
- Google font Roboto was used as the website font
- Balsamiq (https://balsamiq.com/)
- Balsamiq was used to create the website wireframes
- Font Awesome (https://fontawesome.com/)
- Font awesome was used to provide the relevant fonts/icons for the website
- Lucidchart (http:https://lucidchart.com)
- Lucidchart was used to create the database design diagrams
- JQuery (https://jquery.com)
- JQuery was used in some javascript files for DOM manipulation
- TinyPNG (https://tinypng.com/)
- TinyPNG was used to compress images to improve performance and reduce space
- CSS Validation Service (https://jigsaw.w3.org/css-validator/)
- CSS validation service for validation the css in the project
- HTML Markup Validation Service (https://validator.w3.org/)
- HTML validation service for validation the css in the project
- Chrome dev tools (https://developers.google.com/web/tools/chrome-devtools)
- For troubleshooting and debugging of the project code
- Chrome Lighthouse (https://developers.google.com/web/tools/lighthouse)
- For performance, accessibility, progressive web apps, SEO analysis of the project code
- Responsive Design (http:https://ami.responsivedesign.is/)
- Website for generating the responsive image in this README
- JS Fiddle (https://jsfiddle.net/)
- Used for testing html and css concepts
- GitHub Wiki TOC generator (https://ecotrust-canada.github.io/markdown-toc/)
- Used for generating a table of contents for this README
- Google Maps api (https://developers.google.com/maps)
- The Google Maps api is used to display the stadium information on the memory page
- Google maps geocode API (https://developers.google.com/maps/documentation/geocoding/start)
- The Google Maps geocode api is used to translate the event name returned from the memory stadium name value and translate to a Google Maps location on the memory page
- Gofullpage chrome plugin (https://chrome.google.com/webstore/detail/gofullpage-full-page-scre)
- This plugin was used to take full page screenshots for testing images
- Python online interpreter (https://www.programiz.com/python-programming/online-compiler/)
- For testing python code snippets
- Pytest (https://docs.pytest.org/en/6.2.x/)
- For Python unit testing
- UILicious (www.uilicious.com)
- For automated testing
The testing information and results for this project are documented in TESTING.md
The project also uses a number of API's, below are the steps to configure the API in your environment
- Create an account at https://console.developers.google.com
- Create an API key and set up the account as requested by google, for example billing information
- Configure 2 APIs, Maps JavaScript API and Geocoding API
- In the credentials screen, set the web restrictions url referrer to the urls of where the site will be hosted as well as your local development environment
- In the API restrictions section in the credentials screen, limit to 2 APIs: Maps JavaScript API and Geocoding API
- In the memory.html file under the footer, update the Google Maps script src to include your API key
- Create an account at emailjs.com
- In the integration screen in the emailjs dashboard, note your userid
- Create an email service in the Email Services section and note the id
- Create an email template in the Email templates section and note the id
- Update the script sendEmail.js, method sendMail with your user id, email service id and email template id
There are a number of applications that need to be configured to run this application locally or on a cloud based service, for example Heroku
- Create an account at aws.amazon.com
- Open the IAM application and create a new user
- Set the AmazonS3FullAccess for the user and note the users AWS ACCESS and SECRET keys
- Open the S3 application and create a new bucket. For the purpose of this application the bucket name is ci-ms3-football-memories but this can be updated in the util.py route
- With security best practices update the public access and policy bucket to enable the user created and the application access to read/write to the S3 bucket. Consult the AWS documentation if required: https://aws.amazon.com/s3/
- The s3 bucket is now updated to be accessed by your application
- In the util.py route update the variables s3_bucket_name and s3_bucket_url with the correct information that you have set up, for example:
s3_bucket_name = "ci-ms3-football-memories"
s3_bucket_url = "https://ci-ms3-football-memories.s3.eu-west-1.amazonaws.com/"
Mongodb is the database used in the application
- Create an account at mongodb
- Create a database cluster
- Select the cluster, and in the collections section create a database and create 5 collections under the database: memories, comments, ratings, tournaments, users
- In the database access, create a user and allow the user read/write access. Note the username
- In the network access tab, allow network access from the ip-address of the application connecting to the database
- In the Databases section click Connect, and select connect your application
- Note the MONGO_URI, MONGO_DBNAME and user, these parameters are used when deploying locally(env.py file) and deploying on the likes of heroku(config vars)
To run this project locally, you will need to clone the repository
-
Login to GitHub (https://wwww.github.com)
-
Select the repository pmeeny/CI-MS3-FootballMemories
-
Click the Code button and copy the HTTPS url, for example: https://github.com/pmeeny/CI-MS3-FootballMemories.git
-
In your IDE, open a terminal and run the git clone command, for example
git clone https://github.com/pmeeny/CI-MS3-FootballMemories.git
-
The repository will now be cloned in your workspace
-
Create an env.py file in the root folder in your project, and add in the following code with the relevant key, value pairs, and ensure you enter the correct key values
import os
os.environ.setdefault("IP", TO BE ADDED BY USER)
os.environ.setdefault("PORT", TO BE ADDED BY USER)
os.environ.setdefault("SECRET_KEY", TO BE ADDED BY USER)
os.environ.setdefault("MONGO_URI", TO BE ADDED BY USER)
os.environ.setdefault("MONGO_DBNAME", TO BE ADDED BY USER)
os.environ.setdefault("AWS_ACCESS_KEY_ID", TO BE ADDED BY USER)
os.environ.setdefault("AWS_SECRET_ACCESS_KEY", TO BE ADDED BY USER)
-
Install the relevant packages as per the requirements.txt file
-
Start the application by running
python3 app.py
To deploy this application to Heroku, run the following steps.
- In the app.py file, ensure that debug is not enabled, i.e. set to True
- Create a file called ProcFile in the root directory, and add the line
web: python app.py
if the file does not already exist - Create a requirements.txt file by running the command
pip freeze > requirements.txt
in your terminal if the file doesn't already exist - Both the ProcFile and requirements.txt files should be added to your git repo in the root directory
- Create an account on heroku.com
- Create a new application and give it a unique name
- In the application dashboard, navigate to the deploy section and connect your application to your git repo, by selecting your repo
- Select the branch for example master and enable automatic deploys if desired. Otherwise, a deployment will be manual
- The next step is to set the config variables in the Settings section
- Set key/value pairs for the following keys: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, IP, MONGO_DBNAME, MONGO_URI, PORT, SECRET_KEY
- Go to the dashboard and trigger a deployment
- This will trigger a deployment, once the deployment has been successful click on the "Open App" link to open the app
- If you encounter any issues accessing the build logs is a good way to troubleshoot the issue
-
For the memories page, I used some html and css code from https://bootstrapious.com/p/bootstrap-photo-gallery as a basis for the memories gallery
-
For the password validation, I built on examples code described here:
-
For pagination on the memories, tournament I found the following links useful to implement pagination:
-
For the footer to stick to the bottom of the page, I used code from the following pages: -https://stackoverflow.com/questions/4575826/how-to-push-a-footer-to-the-bottom-of-page-when-content-is-short-or-missing#:~:text=Just%20wrap%20your%20.,will%20move%20to%20the%20bottom.
-
My project is built using a Blueprints structure, I found the following video and links invaluable to structure my project accordingly
-
For password validation I re-used some code here: https://stackoverflow.com/questions/9142527/can-you-require-two-form-fields-to-match-with-html5
-
For storing images in an AWS S3 bucket, I found the following links very useful
-
I found the following resource great for some jquery code used: https://api.jquery.com/replacewith/
-
I used html/css code, then tweaked it accordingly for the site footer: https://jsfiddle.net/bootstrapious/c7ash30w/
-
For the send-email functionality I used some code from the code institute module from the course
-
For exception handling I found this page useful: https://stackoverflow.com/questions/33239308/how-to-get-exception-message-in-python-properly
-
Country list (https://www.technicalkeeda.com/html-tutorials/all-countries-drop-down-list-in-html)
- The country list on the registration page
-
Font Awesome (http:https://fontawesome.com)
- The icons used on the site from font awesome
-
Fonts (https://fonts.google.com/)
- The text font(Poppins) is from Google fonts
-
Google Maps (https://www.google.com/maps)
- The Google Maps api is used to display maps in the memory page
-
The terms and conditions and privacy policy page content was copied from https://www.privacypolicies.com/blog/privacy-policy-template/
-
Pexels (https://www.pexels.com/)
- The hero image used throughout the site is form Pexels, https://www.pexels.com/photo/aerial-view-of-soccer-field-1171084/ by user https://www.pexels.com/@mike-468229
-
The 42(www.the42.ie), Irish Independant(www.irishindependant.ie), Tokyvideo(www.tokyvideo.com), Sky Sports(www.skysports.com), The Guardian(www.theguardian.com), The 42(www.the42.ie), This is Anfield(www.thisisanfield.com), Joe.ie(www.joe.ie), Amazon(www.amazon.com), Getty Images(www.gettyimages.co.uk), BBC(www.bbc.co.uk)
- The memory images added to the memories added to the website were taken from the above websites media galleries
- I would like to thank my fiancée Mary for her help, constant support and ideas for the website, my son Liam, and also to my dog Lily for her company during development of the website.
- I would like to thank my mentor Mo Shami for his input, help and feedback.