- CS50 Final Project - Dance App
During the pandemic, my partner and I have been teaching salsa caleña on Zoom for an online dance school. After every class, we send short videos to our students showing what we have taught via Whatsapp. However, after more than a year of classes, these videos are taking up too much space. I built my final project to create a solution for this problem.
I have built an online platform for teachers and students, where teachers can add unlisted YouTube videos and make them visible to selected students. The website has lots of different features, which I will summarise below (see Routes).
My web application was built using Django, JavaScript and Bootstrap.
According the the specification, my project must adhere to the following guidelines:
Your web application must be sufficiently distinct from the other projects in this course (and, in addition, may not be based on the old CS50W Pizza project), and more complex than those.
I believe that my project meets this requirement for the following reasons:
- My project is based on an original idea, that solves a real-life personal problem which has no similarity to any of the projects built as part of the CS50W course.
- The website is built with different user types: admin, student and teacher.
- The New Video page uses the Youtube Data API from Google and
fetch
in JavaScript to get the YouTube video data and dynamically prepopulate the form. I have built this in such a way that the real API key is hidden on my public deployment to GitHub. - I built a simple notification system, which notifies students of new videos and teachers when a new comment is added to one of their videos. The notifications section is completely responsive and updates dynamically using JavaScript.
- The filter on Index (homepage) was built completely from scratch and uses a JavaScript function to update the page to display videos that meet the criteria selected by the different filters on the page.
- On the New Video page I challenged myself to build functionality that allows users to add a new field to the
CalenaStep
model using JavaScript and update aCheckboxSelectMultiple
field on a Django form dynamically.
Your web application must utilize Django (including at least one model) on the back-end and JavaScript on the front-end.
My application was built using Django, including 8 models on the back-end and uses 8 different JavaScript scripts to make dynamic updates on the front-end. All generated information is saved in a database (SQLite by default).
Your web application must be mobile-responsive
Every page and feature of the web application is mobile-responsive and this is achieved using Bootstrap and custom CSS.
resize.js
is used when the screen is resized to create more complex responsive layouts for Index and Notifications.
There are 8 models for the DanceApp database:
User
- An extension of Django'sAbstractUser
model. Stores the logic for account type (staff, student or teacher). Stores the profile picture URL and total unread notifications for the user.Student
- Creates aOneToOne
relationship withUser
for student users.Teacher
- Creates aOneToOne
relationship withUser
for teacher users.Style
- Stores names of video styles.CalenaStep
- Stores names of salsa caleña steps.Video
- Stores videos uploaded by teachers. Holds many relationships with other models (Style
,Teacher
,Student
,User
,CalenaStep
). Thestudent_access
field determines which videos are visible to each student. This model is used in the/videos
API route, when we make aGET
request.Comment
- Stores comments made by users on a video and creates a relationship withVideo
andUser
.Notifications
- Stores notifications for users and creates a relationship withVideo
andUser
. Holds logic for whether a notification has been read or not.
User can log into the website using a valid username and password.
User must enter their username, email address, first name, surname, password and confirm password. The page has the following validation:
- The password must match the confirm password field
- There is no existing user with the username provided
If the details are valid, a new user is created in the User
model with is_student
flag set to True
. A student instance of the user is created using the Student
model.
This page is exactly the same as the Register Student page, only instead it creates a user in the User
model with the is_teacher
flag set to True
and creates a teacher instance of the user using the Teacher
model.
All logic is stored in util.py
as it's shared between both pages.
The user can change their password, and the page has the following validation:
- Your password can’t be too similar to your other personal information.
- Your password must contain at least 8 characters.
- Your password can’t be a commonly used password.
- Your password can’t be entirely numeric.
If the details are valid, the password will be changed, and the user is redirected to Index with a success message.
This page uses the PasswordChangeForm
from Django, which I have customised to include Bootstrap styling in forms.py
.
If the user clicks 'Logout' in the navigation bar, it will log the user out and redirect to the Login page
This page makes a GET
request to the /videos
API route to get all available videos for the logged in user (logic for this is stored in utils.py
). Then, the page uses fetch
in JavaScript to get the JSON video data to display the videos using HTML. The API route uses the following logic:
- Teacher and admin users can see all videos in the database
- Students can only see the videos which teachers have made available to them
This page also contains a filter which uses a JavaScript function to filter the videos returned by the /videos
API. The user can search on name and filter on style, teacher, level and for salsa calena videos: salsa calena step names.
This page is completely responsive and uses the resize.js
file to change the page according to the device size and whether the user has resized the screen.
This page is visible only to teachers and is used to add a new video to the database. The user enters a YouTube URL and using JavaScript, we extract the YouTube ID and make a GET
request to the YouTube Data API.
If no video is found, or the video URL is invalid, an error message is displayed. If a video is found, the form is shown and the YouTube ID
, Title
, Thumbnail URL
and Description
fields are prepopulated using the JSON data returned by the API.
The teacher must fill in the Style
, Level
, Teacher(s)
, Student access
and Class date
fields. The Student access
field holds the logic for which videos each student can see.
If the teacher selects Salsa Calena
as the video's style, the Calena Step
field will dynamically appear which displays all steps that are currently saved in the CalenaStep
model. The teacher is able to add a new step by making a POST
request to the add-step
API route, which adds the new step name to the database. This entire process is done using JavaScript fetch
to update the page dynamically.
When the teacher submits the form, a new instance in the Video
model is saved.
This page can be accessed by clicking on a video from the Index page. It includes an embedded YouTube video and basic information about the video, and the user is able to carry out the following actions:
- Student users can add or remove the video from their 'Saved Videos', by clicking the heart icon. This makes a
POST
request to the/update_favourites/{videoId}
API route andfetch
is used to update the heart icon using JavaScript. - All users can add and delete their own comments:
- Comments are added with a full page reload after submitting the form (using the
/add_comment
route) - Comments are deleted using
fetch
which makes aPOST
request to thedelete_comment/{videoId}
API route and then updates the page using JavaScript based on the response.
- Comments are added with a full page reload after submitting the form (using the
The embedded video, and entire page are completely responsive.
This page is only visible for student users and displays the videos they have selected to be part of their saved videos on the Video page. It works in a similar way to the homepage, but instead it makes a GET request to the videos/saved
API route to get the JSON data to update the page.
When the user logs in, they can see a notification icon in the navigation bar. This number displays the number of 'new' notifications they have, and comes from the unread_notifications
field on the User
model.
When the user clicks on the notification icon, the unread_notifications
field is set to 0 using a POST
request to the API route /reset_notifications_counter
and JavaScript is used to update the notification icon and show the notifications display. For medium and smaller devices, the page will be redirected to the /notifications
route.
In the notifications display, unread notifications are marked in bold. The user can carry out the following actions:
- If the user clicks on a notification, we make a
POST
request to/read_notification/{notificationId}
using JavaScript fetch to mark the individual notification as read in the database and then we take the user to the Video page associated with the notification. - If the user clicks 'Mark all as read', we make a
POST
request to theread_all_notifications
API route and use JavaScript to update the page to show the notifications as read. - On large devices, if the user clicks on 'Notifications', they will be taken to the
/notifications
route, which has the exact same content, but it's displayed on a page rather than a small section overlaying the page.
There are two types of notifications that can be raised on the platform:
- When a user comments on a video on the Video page, the following notification will be raised for the teachers associated with that video: "{User first name} added a new comment on your video {Video title}". If the user that commented is one of the teachers of the video, a notification will not be raised for that user.
- When a teacher uploads a video on the New Video page, the following notification will be raised for all students who have been given access to that video: "{User first name} added a new video: {Video title}".
Summary of files created by me:
danceapp
- main application directory.static/danceapp
contains all static content.images
contains 'no profile picture' image, logo and login icon.css
contains CSS file.js
- all JavaScript files used in project.comments.js
- script that is used invideo.html
template.getvideos.js
- shared script that is imported intovideos.js
andsaved_videos.js
to retrieve all available videos for logged in user.likevideo.js
- script that is used invideo.html
template.newvideo.js
- script that is used innewvideo.html
template.notifications.js
- script that is used in every template as it's part of the baselayout.html
template. Holds logic for notification updates.resize.js
- script that is used in every template as it's part of the baselayout.html
template. Holds logic for some responsive behaviour.saved_videos.js
- script that is used insaved_videos.html
template.videos.js
- script that is used inindex.html
template.
templates/danceapp
contains all application templates.change_password.html
- template for Change Password page.error.html
- template for generic error page.index.html
- template for Index (homepage) which displays all available videos for logged-in user.layout.html
- base template. All other templates extend it.login.html
- template for Login page.newvideo.html
- template for New Video page where teachers can add a new video using YouTube Data API.notifications_block.html
- subtemplate that is used in a couple of other templates withinclude
directive. Contains HTML for notifications.notifications.html
- template for notifications display on medium and smaller devices.register.html
- template for Register Student and Register Teacher page.saved_videos.html
- template for Saved Videos (students only).video.html
- template for individual Video page with embedded YouTube video.
__init__.py
- generated by Django.admin.py
- used to determine models which will be used in the Django Admin Interface.apps.py
- generated by Django.context_processors.py
-notification_processor
creates global context variablenotifications
which is used in the navigation bar defined in thelayout.html
template.forms.py
- defines the model forms used as part of the application (PasswordChangeForm
,NewVideoForm
,CommentForm
).models.py
defines the models used to add to and update the database using Django.tests.py
- generated by Django.urls.py
- defines all application URLs.util.py
- holds logic for getting user videos and registering a new account.views.py
- contains all application views.
finalproject
- project directory__init__.py
.env
- not commited to GitHub, but required to set up locally to store the API key for YouTube Data APIasgi.py
- generated by Djangosettings.py
- generated by Django, also contains logic for messages,notification_processor
, obtaining the API key from the .env fileurls.py
- contains project URLs.wsgi.py
- generated by Django
.gitignore
- defines files to be ignored by Gitdb.sqlite3
- databasemanage.py
- generated by Django.requirements.txt
- packages required in order for the application to run successfully.
First, you will need to set up an API key for the YouTube Data API:
-
Sign in or create an account here https://console.developers.google.com/
-
Create a project in the dashboard
-
Click 'Credentials' in the left sidebar
-
Click 'Create Credentials' at the top of the middle section, and then "API key"
-
Copy this API key
-
In the
finalproject
folder, create an file called.env
-
Suppose my API key is xxx, this file should contain (with no spaces or apostrophes):
YOUTUBE_API_KEY=xxx
-
The Google API should now work successfully. If it doesn't, try another browser.
- Copy the repo to your system.
- Verify you have Python and Django installed on your system. If not you will need to install them.
- Make sure that you have the packages installed from the
requirements.txt
file. - Run the following to start up the Django web server:
python manage.py runserver
- Visit the website in your browser.
- Use the following credentials to log in as a teacher:
username: lucy password: password
- Use the following credentials to log in a student with available videos:
username: becky password: password
- Or, create a new student account by clicking Register in the nav bar.
- Use the following credentials to log in as a teacher:
The solution I have built isn't perfect, as it displays unlisted YouTube videos on the website. The student users can easily go and watch the videos on YouTube and share the links with others. This project is a 'low-cost' solution for teachers with a few students that they trust. It's for people who don't have enough students to make it worthwhile paying for an expensive video-hosting subscription.
- Add student filter on homepage for teachers
- Add clear filters button
- Allow different sorting on index page (most recent, oldest)
- Date validation on New Video form
- Deploy application somewhere
- Edit comment
- Able to edit saved videos list from index / saved videos page
- Make 'Add Comment' work using JavaScript only
- Make notifications icon (total notifications) update in real time
- Change register student to be a single sign on link with email
- Change register student to be a single sign on link with email (and remove register teacher page)