A platform to help Ontario family physicians manage patients who are moving elsewhere in the province, created using Django and Vue.js. Doctors input where they are and where their patient is moving to, and if two compatible listings are found, the doctors are put into contact so that they can try and "swap" their patients who are moving to each others' areas.
FPswap is deployed using PythonAnywhere's free tier at https://sky46.pythonanywhere.com. Please don't use the site unless you're actually an Ontario family doctor though!
This project was made for the Capstone (Final Project) of CS50's Web Programming course.
This project satisfies the distinctiveness and complexity requirements for the final project:
- 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.
- A project that appears to be a social network is a priori deemed by the staff to be indistinct from Project 4, and should not be submitted; it will be rejected.
- A project that appears to be an e-commerce site is strongly suspected to be indistinct from Project 2, and your README.md file should be very clear as to why it’s not. Failing that, it should not be submitted; it will be rejected.
The web app includes user accounts with email verification; an account setting for where a doctor accepts patients from; creation of "listings" of moving patients; archival and deletion of said listings; pairing of moving patients so that doctors can essentially exchange them, occuring once per day; confirmation or denial of swaps; and email notifications upon discovery, acceptance, and rejection of swaps. It combines elements from previous projects such as user accounts, posts/listings, and a Javascript front-end for one specific page (account settings), with new ones that I learned such as sending txt and HTML emails, email verification, front-end using Vue.js, and database tasks that run once every day.
My project is not a social network. People do not use the website to socialize or build relationships. It's a tool to help patients find a doctor who has likely gained a space for a patient in their new area of residence. It is also not an e-commerce site, as obviously nobody is or selling anything. The "exchange" of patients is only a representation of the way doctors with availibilities are found.
This is the base directory of the Django project. It contains four apps: core, authentication, listing, and swap, as well as other general Django files.
-
/doctorswap/manage.py: I didn't create nor edit this file at all, but it is very important and I consider it worth mentioning. This file is used to run all the Django management commands, including those to do with migrations, running the server, and a couple custom commands used to find and modify Swaps. -
/doctorswap/match.py: This file is now unused, but it was where I first created and tested the Python code for finding Swaps, created before I decided on using management commands to run periodic Swap-related tasks. -
/doctorswap/matchreset.py: Another unused file from the earlier stages of development, which was used to undo the work of/doctorswap/match.pywhen testing and debugging. -
/doctorswap/db.sqlite3: This is the database file where FPswap's information is stored. I did not include this file when pushing to GitHub due to it containing personal information used when testing (nothing too sensitive but I'd rather not upload it anyways).
This directory contains the settings and URLs for the Django project as a whole.
-
/doctorswap/doctorswap/settings.py: The Django project's settings. Certain settings are read from environment variables from the.envfile for security reasons. -
/doctorswap/doctorswap/settings_local.py: A different set of the Django project's settings that I use when testing FPswap locally. -
/doctorswap/doctorswap/urls.py: The URLs for the Django project, comprising the URLs of each of the apps as well as the Admin ones.
The core app contains code for the basic parts of FPswap, including the stylesheet; the base.html, index.html, and about.html templates; the index and about views and URLs; custom template tags; and the list of all doctor locations which is used throughout the website.
-
/doctorswap/core/static/styles.scss: The main and only stylesheet for FPswap. At the start of the file, a long list of colours are defined to be used as a palette when styling. -
/doctorswap/core/static/styles.css: The regular CSS stylesheet compiled fromstyles.scssusing Dart Sass. -
/doctorswap/core/static/styles.css.map: A source map file created automatically when compiling the stylesheet. I am unsure of its purpose but it is also possible to compile without creating the map file using the--no-source-mapoption. -
/doctorswap/core/templates/core/base.html: The basic template used by all other templates in the website. Includes the navbar, links to the Bootstrap 5.1 stylesheet and my own, atitleblock, and amainblock. -
/doctorswap/core/templates/core/index.html: The Index page, which displays all the currently active Listings. -
/doctorswap/core/templates/core/about.html: The About page, which has some information on FPswap, how to use it, and how to send feedback/questions. -
/doctorswap/core/templatetags/base_tags.py: Custom template tags for use in the base template. Currently only contains theactivetag, which is used to conditionally render navbar links so that the current page's link has a different style. -
/doctorswap/core/templatetags/alias_tag.py: Custom template tag which is used to assign a variable inside a Django template, used on the Swaps page to make the code for rendering a lot shorter. This does go against Django's design philosophy (assign variables in Python views), but it was the easiest way to solve the problem I was facing, and in my opinion it doesn't negatively affect the readability or organization of my code by a significant amount. -
/doctorswap/core/context_processors.py: Contains the single context processordefault_domain, which allows me to access the domain set in settings inside of templates. This is used when rendering links in emails. -
/doctorswap/core/models.py: Contains no actual models, but instead the list of all Ontario doctor locations to choose from which is used in models throughout the rest of FPswap. -
/doctorswap/core/views.py: The view functions for the Index and About pages, which simply render their respective templates. -
/doctorswap/core/urls.py: The urlpatterns for the Index and About pages.
The authentication app handles user accounts.
-
/doctorswap/authentication/static/authentication/account.js: The JavaScript file used in the account template, made using Vue and built using Vite. -
/doctorswap/authentication/static/authentication/account_old.js: The old version ofaccount.jswhich did not use any build steps. -
/doctorswap/authentication/static/authentication/index.293323fb.css: The styles generated by Vue which are used by the Vue-multiselect component in the account template. -
/doctorswap/authentication/templates/authentication/: Contains the templates for the Account, Login, Register, and email verification views, as well as the email template used for email verification (txt and html versions). -
/doctorswap/authentication/admin.py: Registers theUsermodel in Django's admin interface. -
/doctorswap/authentication/models.py: Contains a customUsermodel that extends Django'sAbstractBaseUser, with the fieldsemail,name,locations,cpso_number,is_active,is_staff,date_joined, andlast_login. This file also containsUserManager, which handles creation of users and superusers. -
/doctorswap/authentication/forms.py: Contains the model formUserForm, which is used when editing user accounts. -
/doctorswap/authentication/urls.py: The URL patterns for theauthenticationapp (account/,login/,register/,logout/,verify/,reset-password/). -
/doctorswap/authentication/views.py: Views for registering, logging in and out, verifying email, resetting password, and editing account. -
/doctorswap/authentication/tokens.py: A token generator used when verifying emails.
-
/doctorswap/listing/templates/listing/: Contains thelistings.htmlandnew.htmltemplates, which display a user's listings and create a new listing respectively. -
/doctorswap/listing/admin.py: Registers theListingmodel in Django's admin interface. -
/doctorswap/listing/models.py: Contains theListingmodel, which represents a moving patient and has the fieldsnew_location,user,active, andcreated_at. -
/doctorswap/listing/forms.py: Contains theListingFormmodel form, which is used when creating new Listings. -
/doctorswap/listing/urls.py: The URL patterns for thelistingapp (listings/,new/,edit/,delete/) -
/doctorswap/listing/views.py: Views for viewing, creating, editing, and deleting Listings.
-
/doctorswap/swap/management/commands/findswaps.py: A management command which finds and creates any possible Swaps between pairs of Listings, also sending emails to the users who created the Listings involved in a Swap. The command ($ python doctorswap/manage.py findswaps) should be run once every 24 hours through some kind of task or otherwise. -
/doctorswap/swap/management/commands/checkswaps.py: A management command which checks for any Swaps that are either 7 or 14 days old. If a Swap is 14 days old, it expires and the Listings are reactivated. The associated users are also notified. If Swap is 7 days old, the Users are notified that it will expire in 7 more days. This command ($ python doctorswap/manage.py checkswaps) should also be run once every 24 hours. -
/doctorswap/swap/templates/swap/: Contains the template for displaying a user's Swaps, as well as the email templates for notifying users of new swaps, confirmed swaps, denied swaps, and expiring swaps (txt and html versions of each). -
/doctorswap/swap/html_email.py: A customsend_html_emailfunction which makes it a lot easier to send emails using both the html and txt templates. -
/doctorswap/swap/admin.py: Registers theSwapmodel in Django's Admin interface. -
/doctorswap/swap/models.py: Creates theSwapmodel, with the fieldslisting1,listing2,active,confirm1,confirm2,deny1,deny2, andcreated_at. -
/doctorswap/swap/urls.py: This app's only URL pattern, which displays a list of all the user's Swaps (swaps/). -
/doctorswap/swap/views.py: This app's only view function, which displays the list of the user's Swaps and allows them to confirm or deny each one.
- This is where all the static files are collected using the
collectstaticmanagement command to be served in production.
- This is the base directory of the Vue.js app I used for the Account page, which I later compiled into a single JavaScript file using Vite and copied to
/doctorswap/authentication/static/authentication/account.jsin the main Django project. Most of the files are created by default when I created the Vue application. My actual Vue code is located at/doctorswap-account/src/main.jsand/doctorswap-account/src/components/Multiselect.vue. I used the Vue-multiselect project for editing the user's accepting-patients-locations and also added some reactivity to the editing of user accounts.
To run FPswap locally:
-
Download and unzip the folder.
-
Install the Python dependencies listed in
requirements.txt. -
In the
doctorswap/doctorswap/directory, set the following environment variables:EMAIL_HOST_USERto the email address of the account you want to send emails from;EMAIL_HOST_PASSWORDto the password of said email account (for Gmail, this has to be an app password); andSECRET_KEYto a Django [secret key] (https://docs.djangoproject.com/en/4.1/ref/settings/#std-setting-SECRET_KEY). -
Open a terminal window inside the base
doctorswapdirectory. -
Run
$ python manage.py makemigrations, then$ python manage.py migrateto create the database and make migrations. -
Run
$ python manage.py runserver --settings=doctorswap.settings_localto run the server withsettings_local.py. -
Set up some kind of tasks to run each of the the following commands once per day:
$ python manage.py findswaps$ python manage.py checkswaps
The site should now be running on your server/device! Note that I have only run FPswap on my local computer and PythonAnywhere, the latter having a quite different deployment process.