e-Health

forthebadge made-with-python

Prologue

Video demonstration

To watch the video, click here

Running this program

By using this project, run python e_health_main.py in your terminal.

For Windows users, we recommend using Windows Terminal to run the program.

Open your terminal in full screen for a better user experience.

GitHub Page of this project

We recommend opening the following information on your browser by clicking here.

Table of content

Key information

Library dependencies

The project is written with Python and can be run with Python v.3.7.9 or higher (click here to download). Except Python built-in packages, if you do not have the following libraries on your computer, you will have to install them to run the program:

We implemented error handling on library dependencies when running e_health_main.py. The user will just have to follow the above or on screen instructions.

The user can also run pip install -r requirements.txt to install everything at once. However we strongly recommend to create a virtual environment to install dependencies and run the program.

Virtual environment

Generally there are two approaches:

If you no longer need this environment, run deactivate and just delete the env folder.

Test accounts

In order to use the program, we registered multiple test accounts with known passwords.

We recommend using the following credentials to login since:

Admin

id Name Email Password
1 Admin Admin admin@ehealth.com root@EH_24

GP

id Name Email Password
16 Louis Pasteur louis.p@gmail.com ferment1858
17 Edward Jenner edward.j@msn.com 17@vaCCi*98
18 Joseph Lister joseph.l@outlook.com anti&1865/septic

Patient

id Name Email Password
51 Hermione Granger hermione.g@gryffindor.com Lumos#9Reparo
52 Ron Weasley ron.w@weasley.com wizardschess100
53 Harry Potter harry.potter@hogwarts.com Expecto^Patronum7

SQLite database

When downloading the zip file of this project, the database is already initialized and no further action is required. However, it is possible to perform the below mentioned actions via running the explicit scripts.

The database must be systematically taken down before being initialized.

Development

User experience diagrams

</tr>
Patient User Journey
GP User Journey
Admin User Journey

Entity relationship diagram

Extensibility

Implemented:

Future work:

Program design

Classes

Outline

In the following section the different class methods are described in detail, including:

Return Variables

Some methods (i.e. most CRUD methods) do not return anything.

Others (i.e. CRUD) by definition do return something. For these:

Further Information

Consult the classes themselves for additional information.

Each method has a docstring describing its purpose, parameters and return values, as well as how various edge cases are handled.

Appointment

Name Type User flow & purpose Parameters Returns
book instance - Admin, Patient
- Booking an appointment from an instance (instance created in user flow)
- -
update instance - Admin, GP
- Updating an appointment’s details (technically overriding every DB attribute w/ instance values)
- -
select factory - Admin, GP
- Generating an instance of an appointment to later update attributes based on user input
- booking_id - Appointment instance
- DF incl. indexing of all appointment attributes (not incl. prescription)
select_GP static - Admin, GP
- Getting a list of a GP’s appointments in a given time period
- type = day/week
- gp_id
- [time parameters]
- DF of a specific GP’s appointments for an upcoming day (detailed) or week (less detailed per day)
select_GP_pending static - GP
- Getting a list of a GP’s pending (awaiting confirmation) appointments
- gp_id - DF of a specific GP’s pending appointments incl. all relevant attributes (sorted by booking_start_time ASC)
select_patient static Used in Record.select() method - patient_id
- timeframe (‘previous’/’upcoming’)
- status = None (can be ‘confirmed’ for a specific DF for Record)
- DF of a specific patient’s previous and upcoming appointments depending which one we need. DF is displayed with Appointment relevant information for a specific Patient (incl. ID and other relevant attributes)
select_availability static - Admin, Patient
- Getting a specific GP’s availability before booking an appointment
- type = day/week
- gp_id
- [time parameters] start date specifically
- DF incl. indexing of a specific GP’s availability for an upcoming day (detailed) or week (less detailed per day). DF shows times when the GP is unavailable and proved indexes to select from and book an appointment
NB: in user flow, to ‘check’ for availability count rows in DF
select_other_availability static - Admin, Patient
- Getting alternative GP availabilities when a patient’s own GP has none before booking an appointment
- type = day/week
- gp_id
- [time parameters]
- DF incl. indexing w/ all other GPs’ (i.e. with gp_id not equal to the gp_id parameter passed) availability for an upcoming day (detailed) or week (less detailed per day)
NB: in user flow, to ‘check’ for availability count rows in DF
change_status static - Admin, Patient, GP
- Changing status for different reasons e.g. cancelling, confirming, rejecting
- booking_id
- new_status
-
confirm_all_GP_pending static - GP
- Confirming all pending appointments
- gp_id -

GP

Name Type User flow & purpose Parameters Returns
insert instance - Admin
- Inserting a new GP from an instance populated by user input (GPs cannot register themselves; instance created in user flow)
- -
update instance - Admin
- Updating a GP’s details (technically overriding every DB attribute w/ instance values)
- -
select factory - Admin
- Generating an instance of a GP to later update attributes based on user input
- gp_id - GP instance

Generally: DF incl. indexing of all of a GP’s attributes (except password)
- df_object
- df_print
select_list static - Admin
- List of GPs to choose from (used in multiple branches)
- type = all/ active/ not_full Generally: DF of all relevant GPs {gp_id, name (Dr. + gp_last_name), gp_birth_date, no. patients (if type = ‘not_full’; sort column)}
- df_object
- df_print
select_table static - Admin
- List of GP departments/specialisations for reference when updating
- type = department/ specialisation Generally: DF of relevant DB table
- df_object
- df_print
reallocate_patients static Used in GP.change_status() and GP.delete() methods - gp_id - BOOL True (successful) or False (unsuccessful)
- Failure details (see docstring and method)
reallocate_appointments static Used in GP.change_status() and GP.delete() methods - gp_id - BOOL True (successful) or False (unsuccessful)
- Failure details (see docstring and method)
change_status static - Admin
- Changing a GP’s status (to inactive/active)
- gp_id
- new_status
- BOOL True (successful) or False (unsuccessful)
- Failure details (see docstring and method)
delete static - Admin
- Deleting a GP
- gp_id - BOOL True (successful) or False (unsuccessful)
- Failure details (see docstring and method)

Patient

Name Type User flow & purpose Parameters Returns
update instance - Admin
- Updating a patient’s details (technically overriding every DB attribute w/ instance values)
- -
select factory - Admin, Patient
- Generating an instance of a patient to later update attributes based on user input
- patient_id - Patient instance

Generally: DF incl. indexing of all of a patient’s attributes (except password, and medical conditions neither as that’s for GPs to edit)
- df_object
- df_print
select_list static - Admin
- List of patients to choose from (used in multiple branches)
- type = pending/matching
- if matching, patient_last_name
Generally: DF of all relevant patients {patient_id, default GP (if type = ‘matching’), patient_first_name, patient_last_name, patient_birth_date, patient_registration_date (if type = ‘pending’; sort column)}
- df_object
- df_print
select_gp_details static - Patient
- Retrieving a patient’s default GP ID and name (for booking & sharing with patient)
- patient_id - gp_id
- gp_name
change_gp static - Admin, Patient
- Changing a patient’s default GP (checks GP not full first)
- type = auto (least full)/specific
- patient_id
- if specific, new_gp_id
- BOOL True (successful) or False (unsuccessful)
- new_gp_name (=None if BOOL == False for simpler user flow coding)
confirm static - Admin
- Confirming patients (currently no direct method to change status to ‘inactive’, but allowed in DB)
- type = all/single
- if single, patient_id
NB: patients were automatically given a GP during registration to avoid allowing nulls in the DB
delete static - Admin
- Deleting a patient
- patient_id -

Prescription

Name Type User flow & purpose Parameters Returns
insert instance - GP
- Inserting a new prescription from an instance populated by user input (instance created in user flow)
- -
select_patient static Used in Record.select() method - patient_id Generally: DF with details of a patient’s prescriptions {drug_name, drug_dosage, drug_frequency_dosage, prescription_expiry_date (YYYY-MM-DD), booking_id} whose booking status is “attending”
- df_object
- df_print
select_drug_list static - GP
- Getting a list of drugs to choose from (for a prescription)
- Generally: DF with all drugs {drug_id, drug_name}
- df_object
- df_print

Record

Name Type User flow & purpose Parameters Returns
update instance - GP
- Updating a patient’s medical record (technically overriding every DB attribute w/ instance values)
- -
select factory - Admin, GP
- Generating an instance of a patient record to later update attributes based on user input. - Whilst lots of patient information is displayed, only ‘conditions’ and ‘appointment notes’ are editable (assume prescriptions are non-editable/revokable)
- patient_id - Record instance

Generally: 2 DFs incl. indexing of all of a patient’s ‘medical’-related details: 1) attributes & medical conditions 2) previous appointments & corresponding prescriptions
- df_patient_object
- df_patient_print
- df_apps_object
- df_apps_print
select_conditions static - GP
- List of possible medical conditions for reference when updating patient record
- Generally: DF of all possible medical conditions
- df_object
- df_print

Schedule

Name Type User flow & purpose Parameters Returns
select static - Admin, GP
- Viewing a GP’s schedule
- gp_id
- type = ‘day’ or ‘week’
- start_date (YYYY-MM-DD)
Generally: DF of a specific GP’s schedule for a given day (detailed: booking_id, booking_agenda, booking_type, (patient_first_name, patient_last_name, patient_id)) or week (less detailed: x-axis: days, y-axis: timeslots)
- df_object
- df_print
- df_print_morning
- df_print_afternoon
select_timeoff static - Admin
- Viewing a GP’s upcoming timeoff
- gp_id
- type= ‘all’, ‘past’ or ‘upcoming’
Generally: DF of a GP’s upcoming time off {booking_start_time (YYYY-MM-DD), booking_status}
- df_object
- df_print
check_timeoff_conflict static - Admin, GP
- Checking proposed GP timeoff doesn’t conflict with any appointments
- gp_id
- date_start (YYYY-MM-DD)
- date_end (YYYY-MM-DD)
BOOLean: ‘True’ if there was a conflict, ‘False’ is there was no conflict
- boolean
DF of conflicting appointments {booking_id, booking_start_time, booking_status}
- df_object
- df_print
insert_timeoff static - Admin, GP
- Inserting GP time off (only whole days possible), if there is no conflict according to check_timeoff_conflict
- gp_id
- timeoff_type = ‘time off’ or ‘sick leave’
- start_date (YYYY-MM-DD)
- end_date (YYYY-MM-DD)
- same return as check_timeoff_conflict
delete_timeoff static - Admin, GP
- Deleting a GP’s upcoming time off (e.g if no longer sick, holiday cancelled) (only whole days possible)
- gp_id
- type = ‘all’ dates or ‘custom’ date range
- timeoff_type = ‘time off’, ‘sick leave’ or None = ‘all’
- start_date (YYYY-MM-DD, None for type = all)
- end_date (YYYY-MM-DD, None for type = all)
- no return, just deletion

User

Database

Why SQLite?

We decided to rely on a SQLite database because of the following reasons:

Database description

For a detailed description of our database, we would like to refer to our ER diagram.

Important points to mention:

Dummy data

We have created extensive dummy data so that our system can be:

Our dummy data is stored within the dummy_data folder.

Database execution

When downloading the zip file of this project, the database is already initialized and no further action is required. However, it is possible to perform the below mentioned actions via running the explicit scripts.

The database must be systematically taken down first before being initialized.

Nested dictionaries

The menu navigation was implemented using nested dictionaries. All dictionaries were formatted the same way in order to be read by a common method, following the below template:

dictionary = {"title" : [menu title] ,
                             "type" : [“main” or “sub”] ,
                             "1" : ( [Option 1 text] , [Corresponding method] , [Following dictionary] ),
                             "2" : ( [Option 2 text] , [Corresponding method] , [Following dictionary] ),
                             "3" : ( [Option 3 text] , [Corresponding method] , [Following dictionary] ),
                             ... }

The dictionaries and corresponding methods are stored within the user_menu_flow folder.

Displaying menus

A single and common method display() was designed to manage the entire system menus navigation. It is stored in utils.py.

The method utils.display() works the following way:

User input menus

However, some specific menus could not be generated by the display() method in the manner described above. To illustrate, it was the case for menus requiring specific user input such as the ‘Register’ menu.

For specific menus, the menu display and user input combined with input validation are inserted within the ‘corresponding method’ of the previous menu dictionary. Once the task is completed, the method just has to call utils.display([following dictionary]) just like the other methods.

Statistics

-------------------------------------------------------------------------------
Language                     files          blank        comment           code
-------------------------------------------------------------------------------
Python                          18           1472           2091           4188
Markdown                         9            422              0            781
-------------------------------------------------------------------------------
SUM:                            27           1894           2091           4969
-------------------------------------------------------------------------------