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.
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:
pip install pandas
).pip install tabulate
).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.
Generally there are two approaches:
If you are using conda, you can simply create an environment and activate it with this official tutorial.
Otherwise, if you choose venv to create an virtual environment, it will be a little complicated and we’ve written the steps for you to follow:
cd env
in terminal;python -m venv .
(or python3 -m venv .
, this depends on your aliases setting in your operating system, don’t forget .
at the end), and now you should have pip and a core python virtual environment;cd Scripts
(Scripts is already generated under env) and use the platform-specific script (as shown in the pic attached) to activate the environment.If you no longer need this environment, run deactivate
and just delete the env
folder.
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 | Password | |
---|---|---|---|
1 | Admin Admin | admin@ehealth.com | root@EH_24 |
GP
id | Name | 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 | 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 |
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.
Implemented:
Future work:
In the following section the different class methods are described in detail, including:
Some methods (i.e. most CRUD methods) do not return anything.
Others (i.e. CRUD) by definition do return something. For these:
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.
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 | - |
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) |
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 | - |
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 |
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 |
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 |
We decided to rely on a SQLite database because of the following reasons:
For a detailed description of our database, we would like to refer to our ER diagram.
Important points to mention:
We have created extensive dummy data so that our system can be:
Our dummy data is stored within the dummy_data folder.
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.
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.
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:
Takes 1 dictionary as argument.
Print the string associated to the title
key of the dictionary as the menu title.
Print the user options as the dictionary key followed by the option text:
[1] Option 1 text
[2] Option 2 text
…
If the dictionary type
key is “sub”, print the go back option:
[#] Go back to main menu
If the user is logged in, print the logout option:
[X] Logout
If the user is not logged in, print the exit system option:
[E] Exit the system
Request user input, if the user input is invalid, print error message and request input again.
utils.display(
[following dictionary])
to proceed to the next menu.If the user choice is to ‘go back to main menu’, execute a recursive call of the display()
method with the correct main menu dictionary as argument.
If the user choice is to logout, call logout()
and execute a recursive call of the display()
method with the main register/login menu dictionary.
sys.exit()
.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.
-------------------------------------------------------------------------------
Language files blank comment code
-------------------------------------------------------------------------------
Python 18 1472 2091 4188
Markdown 9 422 0 781
-------------------------------------------------------------------------------
SUM: 27 1894 2091 4969
-------------------------------------------------------------------------------