Welcome to Hurtlocker Sports Club.
The goal of this project is to create a web application for a sports club so that they can recruit new members and securely, and easily accept subscriptions from members. The app will provide members with information regarding the club and its activities. Users will be able to browse the different activity types and make bookings when they have purchased a membership subscription. When they purchase a subscription they will be able to access list of the upcoming events associated with each activity and in turn will be able to make bookings and attend the sessions.
The app was deployed using Heroku and can be found here: Hurtlocker
- Providing a platform to publicise the club and in turn assists with recruiting new members and collecting subscriptions from them.
- Serving as a place where the club can display basic info about the club, its activities and schedules.
- EThe app will also allow staff members of the club schedule sessions and make them available for booking.
- Providing a platform for them to see what activities are available and what benefits subscribing would have.
- Enabling users to sign up and easily pay for their subscriptions.
- Presenting useful information to members regarding the club, its activities and schedule.
- Allows paid members to book activities through the site and in turn attend the sessions.
As a user:
I want to be able to create an account
I want to view the various activities the club caters for and what a membership allows me to do.
I want to sign up for membership and pay for it securely.
I want to see more in depth information regarding the clubs activities and schedule.
I want to book activities.
I want to view my upcoming bookings for activities and cancel if needs be.
I want to create a profile for my user and customize it accordingly.
I want to cancel my subscription if I need to.
I want to view my subscription end date/renewal.
As an admin:
I want to see subscribers to the club and when they are subscribed until
I want to update the activity information and add,edit and delete new activities and update their schedules.
I want to see which members have booked which activities.
I spent a lot of time initially thinking about the design of the website for Hurtlocker.
I went for darker colors of black and dark red along with a slightly mustard yellow to make things stand out a bit.
The theme fits that of a martial arts gym.
I used Google Fonts to explore the various options available and went for some basic clean fonts.
For the titles and subtitles, I went with the font Righteous and for the main text I have used Roboto Condensed.
I used flaticon for the logo and Font Awesome for all other icons throughout.
I utilised Coolors](https://coolors.co/) to pick the colors for the site.
The site hero image had a lot of red and black so I decided to stick with that sort of theme.
The site is structured in such a way that users can find relevant information, subscribe and make bookings easily.
The three main sections being the initial landing and club page, the activities section and the profiles section.
The navbar at the top makes navigating the site easy and there are various handy links throw in relevant places also.
The data model was quite a simple one with most relationships being one to many other than the bookings table.
Overall it didn't take much time to implement this and Django models really made it very quick.
Current features for the user include:
- User login/registration via email
- Subscription payments through Stripe
- Subscription status and subscription end can be viewed
- Subscriptions can be cancelled and renewed from the stripe checkout window
- Profile pictures can be uploaded
- Training sessions can be booked and cancelled
- Existing bookings can be viewed
- Display of the sessions available for booking
Current features for the admin include:
- All members and bookings can be viewed by admins
- Activities can be added, edited and deleted.
- Booking slots for sessions can be added and deleted allowing admins full control of the clubs schedule.
Future features for the user include:
- User login/registration via social media or other accounts
- A club forum for members to discuss club activities
- Instructors assigned to activities to manage them themselves for scalability
- Create different tiers of membership for the varying levels of members
Future features for the admin include:
- Admin panel for each activity instructor to manage schedules and booking numbers
- Ability to cancel a members subscription should they break club rules
- HTML
- CSS
- Javascript
- Python
- Bootstrap 4.6 was used for the key layout components
- Django is the Python framework the backend code uses
- SQLite3 was used for data storage in development
- boto3 ensures that the app works with Amazon S3
- dj-database-url allows the app to work with PostgreSQL
- django-allauth is used for all user authentication
- django-crispy-forms ensures that all Django forms are rendered with Bootstrap styling
- django-storages gives the functionality in a custom storage class needed to connect to S3
- gunicorn is the WSGI server that runs the Python app
- oauthlib ensures the Google OAuth integration works with Allauth
- Pillow for image handling on forms
- psycopg2-binary is the PostgreSQL database adapter for Python
- Font Awesome is the source of all icons
- Lucidchart was used to plan the relational database models
- Balsamiq was used to sketch the wireframes
- Stripe provides the technology for processing payments
- Gmail is used to send emails to users
- Amazon S3 is used for CSS and media storage
- Gitpod was the IDE used for writing all code
- Heroku is the cloud-hosting service the app is hosted by
- PostgreSQL is the database used, provided by Heroku
Extensive testing was carried out on all aspects of the app to ensure that it functions smoothly.
All HTML was checked using the W3 validator.
All CSS was checked on W3C and passed without errors.
All JavaScript was tested on Esprima and no errors were returned.
All Python code was checked with PythonChecker.
Test | Action | Expected Outcome | Pass/Fail |
---|---|---|---|
I want to be able to create an account | Sign up using my email address | Email verification screen comes up next and email gets sent to my inbox | Pass |
I want to view the various activities the club caters for and what a membership allows me to do. | Viewing the club page unsubscribed | I can see all the clubs activities and their schedules and a description of the club and its membership offering | Pass |
I want to sign up for membership and pay for it securely. | Using the signup page to join brings me to a checkout summary which then creates a secure checkout session with Stripe | Checkout is successful and a welcome screen is shown, with a link to the club page. Welcome email and receipt of payment email received | Pass |
I want to manage my subscription and cancel if I need to. | Heading to the profile page and click manage subscription | A session is created with Stripe and I can cancel or renew the subscription | Pass |
I want to create a profile for my user and customize it accordingly. | Heading to the profile page using the link on the navbar and edit profile | i can change my name and upload a new profile picture | Pass |
I want to book activities. | Heading to the page of the activity that I wish to book | I can view the booking slots that are available and by simply clicking on the icon I book the session and a notification appears | Pass |
I want to view my upcoming bookings for activities and cancel if needs be. | Navigating to my profile page to view my bookings and clicking the cancel icon next to bookings | I can view all the bookings on my account and when I click the cancel icon the booking is cancelled and a notification appears to let me know | Pass |
I want to view my subscription end date/renewal. | Navigating to my profile page to view my subscription end date/renewal | I can clearly see the date my subscription will end at the bottom of my profile page | Pass |
Test | Action | Expected Outcome | Pass/Fail |
---|---|---|---|
I want to see subscribers to the club and when they are subscribed until | Navigating to the profiles view to view all profiles of members | Subscription end dates will be visible if they have cancelled their subscriptions | Pass |
I want to update the activity information and add,edit and delete new activities and booking slots. | Navigating to the activity that I wish to edit I click on the edit button | I can edit the activity description and image and add or remove booking slots and in doing so receive notification messages | Pass |
I want to see which members have booked which activities. | Navigating to the members profile page | I can see all of the bookings they have made and for which activities | Pass |
Test | Action | Expected Outcome | Pass/Fail |
---|---|---|---|
A user should not be able to book a session for the same time as an existing booking | Navigating to the activity screen and trying to book a session wthat clashes with an existing booking | The booking won't go through and a notification will let the user know they have a session already booked for this time | Pass |
A user should not be able to access anything more than the club page without logging in | Testing any URL without being logged in | The user should be directed to the login page | Pass |
A user should not be able to access the activity pages until they have subscribed | Before paying, they cannot see anything other than the membership registration form, profile page and the club page | After paying, they have access to the activity pages and can make bookings | Pass |
A user should not be able to access any admin functionality such as editing activities or viewing all profiles | Navigating to the is_staff protected views and entering their urls | The users are directed back to the club page and cannot access and admin functionality | Pass |
The app has been tested on a variety of different devices and browsers.
iPhone 5, 6/7/8, 6/7/8 plus, iPad, various android phones, laptops and a 4K monitor were all simulated in Chrome Developer Tools to check for responsiveness. The app held up well and all the elements were consistently positioned where they should be
The app performs exactly as intended on a 21.5 inch iMac on Safari, Chrome and Firefox.
To get the app deployed to Heroku, I took the following steps:
- Navigated to Heroku and signed in
- Created an app by clicking New in the top right and then create new app
- Named the app and select the region closest to you
- In the Resources tab, selected Heroku Postgres and chose a plan
- Ensured that dj_database_url is installed by typing
pip3 install dj_database_url
in the terminal - Also installed
pip3 install psycopg2-binary
- Froze all the requirements with
pip freeze > requirements.txt
- Navigated to the Settings tab on the Heroku site for the app, clicked on Reveal Config Vars and copied the
DATABASE_URL
value - Pasted the url from step 8 into the
os.environ.setdefault("DATABASE_URL", "url_pasted_here")
in myenv.py
file - Dumped all my test data I had input (users, workouts, and pictures) into a json file with a command in the terminal:
./manage.py dumpdata --exclude auth.permission --exclude contenttypes > db.json
- Updated my
settings.py
file by commenting out theDATABASES
section and adding the following block of code -DATABASES = {'default': dj_database_url.parse(os.environ.get('DATABASE_URL'))}
- In the terminal, I ran
python3 manage.py migrate
- Uploaded the json file from step 10 to the Postgres database with
./manage.py loaddata db.json
- Created another superuser so I could log in -
python3 manage.py createsuperuser
- Installed Gunicorn to act as the web server -
pip3 install gunicorn
and repeated step 7 - Created the Procfile to ensure that Heroku could create a web dyno to serve the app
- Temporarily disabled collect static with the terminal command
heroku config:set DISABLE_COLLECTSTATIC=1 --app overreach
- this appeared in the Heroku Config Vars - Added the hostname of the Heroku app to
ALLOWED_HOSTS
insettings.py
- Pushed the repository to Github
- Navigated to the Deploy tab in Heroku and selected Connect to Github
- Enabled automatic deploys from the master branch of the repository
Admin Account Login email:[email protected] password:johnrambo99
After the above steps I needed to get Amazon S3 set up to store static files and images. This is the process I followed:
- Created an account on Amazon AWS
- Searched the services available for S3
- Created a new bucket that was set to be public
- Turned on static website hosting for that bucket
- Input the required CORS configuration -
[ { "AllowedHeaders": [ "Authorization" ], "AllowedMethods": [ "GET" ], "AllowedOrigins": [ "*" ], "ExposeHeaders": [] } ]
- Created a bucket policy with the generator provided by S3 and pasted it into the bucket policy editor
- Ensured that there was access to all objects for everyone in the public access section
- Navigated to Amazon's Identity and Access Management
- Created a Group for management of the Overreach app and a pre-built AWS policy for that group
- Pasted the ARN from the Amazon bucket into the Group policy to allow that group access
- Asigned the correct Group policy to the Overreach management Group
- Created a user for the Group and gave that user full access
- Downloaded the CSV file containing the Access Key ID and Secret Access Key
- Saved the keys from step 13 to my
env.py
file and Heroku Config Vars and also added another varible in Heroku only -USE_AWS
set toTrue
- Removed the
DISABLE_COLLECTSTATIC
variable from Heroku Config Vars - In the terminal of my workspace, I installed boto3 and django-storages
- I also added the following block of code to
settings.py
---- this was to ensure that in production, Django would upload all the static files to S3 as well as cache the static files for faster performance for the userif 'USE_AWS' in os.environ: # Cache control AWS_S3_OBJECT_PARAMETERS = { 'Expires': 'Thu, 31 Dec 2099 20:00:00 GMT', 'CacheControl': 'max-age=94608000', } AWS_STORAGE_BUCKET_NAME = 'hurtlocker-jtb' AWS_S3_REGION_NAME = 'eu-west-1' AWS_ACCESS_KEY_ID = os.environ.get('AWS_ACCESS_KEY_ID') AWS_SECRET_ACCESS_KEY = os.environ.get('AWS_SECRET_ACCESS_KEY') AWS_S3_CUSTOM_DOMAIN = f'{AWS_STORAGE_BUCKET_NAME}.s3.amazonaws.com' # Static and media files STATICFILES_STORAGE = 'custom_storages.StaticStorage' STATICFILES_LOCATION = 'static' DEFAULT_FILE_STORAGE = 'custom_storages.MediaStorage' MEDIAFILES_LOCATION = 'media' # Override static and media URLs in production STATIC_URL = f'https://{AWS_S3_CUSTOM_DOMAIN}/{STATICFILES_LOCATION}/' MEDIA_URL = f'https://{AWS_S3_CUSTOM_DOMAIN}/{MEDIAFILES_LOCATION}/'
- Created
custom_storages.py
and used the classes from django-storage - Commited all the changes and pushed to Github - Heroku then built the app and everything from
/static
was uploaded successfully to S3
The final key step in deployment was to ensure that users could receive emails for various notifications regarding signing up, payment and membership cancellations.
The webhook_handler.py
file in the memberships app takes care of this, but the following lines below were added to the settings.py
file:
if 'EMAIL_HOST_USER' in os.environ:
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_USE_TLS = True
EMAIL_PORT = 587
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_HOST_USER = os.environ.get('EMAIL_HOST_USER')
EMAIL_HOST_PASSWORD = os.environ.get('EMAIL_HOST_PASS')
DEFAULT_FROM_EMAIL = '[email protected]'
else:
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
DEFAULT_FROM_EMAIL = '[email protected]'
In Gmail, I created an app password for the Django app and input the 16 digit password into the Heroku Config Vars with the name EMAIL_HOST_PASS
, corresponding to the clip from settings.py
above. Also, in Heroku, EMAIL_HOST_USER
was set as my own email address in Config Vars.
The project was created in Gitpod and the repository is stored at Github. If you would like to clone the project to run locally on your machine, please follow the steps below:
- Going to the repository on Github
- Clicking on the "Code" button at the top right
- The link to be copied will then be displayed
- In your IDE, ensure you are in the correct directory and then type "git clone" followed by pasting the link
- The repository will then be cloned into your chosen directory
Once you have the cloned repository ready to go, there are a few more steps to take that are crucial to getting it running locally:
- Install all the project's dependencies with the command:
pip3 install -r requirements.txt
- Create a file called
env.py
- thenimport os
at the top and add the following variables to it:os.environ.setdefault("SECRET_KEY", "your_django_secret_key") os.environ.setdefault("STRIPE_SECRET_KEY", "your_stripe_secret_key") os.environ.setdefault("STRIPE_PUBLIC_KEY", "your_stripe_public_key") os.environ.setdefault("STRIPE_WH_SECRET", "your_stripe_webhook_secret")
- Ensure that these variables are properly referenced in your project level
settings.py
file - At this step you can also create a couple of other commented out variables in
env.py
for later on that are necessary to deploy the project:os.environ.setdefault("DATABASE_URL", "for_your_postgres_database") os.environ.setdefault("AWS_ACCESS_KEY_ID", "for_your_amazon_aws_access_key_id") os.environ.setdefault("AWS_SECRET_ACCESS_KEY", "for_your_amazon_secret_access_key")
- Ensure that your
env.py
file is in your.gitignore
file so no secret keys are exposed
Now you'll need to make sure that the database files are ready by using the following commands (in the order listed below) in the terminal:
python3 manage.py makemigrations
python3 manage.py migrate
The app is nearly good to run locally. Next create a superuser so you can log into the admin section with python3 manage.py createsuperuser
and finally start the server with python3 manage.py runserver
If Port 8000 is running you should be able to launch the app in your browser. Be sure to make the port public if you want to start testing Stripe Webhooks.
This time I am happy to take most of the credit myself.
Although as always I've had the big hitters
[Peter and Sush from dt-squad, Alex and Adam from the Creative Underground Project] in the background to help if I ever needed them.
Oh and a special shoutout, even though she'll never see this, I'd like to say thanks to my missus Miriam for feeding me and putting up with my drama for the past year.