diff --git a/SECURITY.md b/SECURITY.md index abdd51b..87cdf98 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,14 +2,14 @@ ## Supported Versions -Right now, only the latest release is being patched for security and performance updates as they are pre-released versions. +Right now, only the latest release is being patched for security and performance updates as they are pre-released versions. The higher the 'x' in the release, the newest release that will contain new patches or adjustments. | Version | Supported | | ------- | ------------------ | -| 0.4.0 | :white_check_mark: | -| 0.3.0 | :x: | -| 0.2.0 | :x: | -| 0.1.0 | :x: | +| 0.4.x | :white_check_mark: | +| 0.3.x | :x: | +| 0.2.x | :x: | +| 0.1.x | :x: | ## Reporting a Vulnerability @@ -17,4 +17,4 @@ If you discover a minor security vulnerability, please submit it to our GitHub i For major vulnerabilities or if you prefer, you can also email me directly at tylerlightwood071@gmail.com with "SECURITY VULNERABILITY" in the subject line. Please include the vulnerability issue (title) and, if applicable, the issue number in the subject line. -We will strive to acknowledge your report promptly and keep you informed of its progress and resolution. \ No newline at end of file +We will strive to acknowledge your report promptly and keep you informed of its progress and resolution. diff --git a/add_animal.py b/add_animal.py index 2c7bc4a..918983c 100644 --- a/add_animal.py +++ b/add_animal.py @@ -1,7 +1,8 @@ import time from colorama import Fore, Style -from common_functions import clear_screen, log_action, get_mongodb_uri, print_animal_table, load_animal_data +from common_functions import clear_screen, log_action, get_mongodb_uri, load_animal_data from sudo_user import sudo_user +from tables import print_animal_table from pymongo import MongoClient # Connect to MongoDB @@ -45,19 +46,19 @@ def add_animal(): # Validate input fields if not all([name, species, breed, gender, age]): print(Fore.RED + "\nInvalid input. All fields are required." + Style.RESET_ALL) - input(Fore.GREEN + "Press Enter to continue..." + Style.RESET_ALL) + time.sleep(2) continue # Validate gender fields if gender.lower() not in ["male", "female"]: print(Fore.RED + "\nInvalid input. Gender must be 'Male' or 'Female'." + Style.RESET_ALL) - input(Fore.GREEN + "Press Enter to continue..." + Style.RESET_ALL) + time.sleep(2) continue # Validate age as positive integer if not age.isdigit() or int(age) <= 0: print(Fore.RED + "\nInvalid age. Please enter a positive integer." + Style.RESET_ALL) - input(Fore.GREEN + "Press Enter to continue..." + Style.RESET_ALL) + time.sleep(2) continue age = int(age) @@ -81,7 +82,7 @@ def add_animal(): # Confirm successful addition of the animal print(Fore.GREEN + "\nāœØ Animal added successfully! āœØ" + Style.RESET_ALL) - log_action(current_user, f"Exited 'Add an animal'") + log_action(current_user, "Exited 'Add an animal'") time.sleep(2) # Ask if the user wants to add another animal diff --git a/common_functions.py b/common_functions.py index d2377d4..e4d1934 100644 --- a/common_functions.py +++ b/common_functions.py @@ -89,28 +89,4 @@ def get_input(prompt): return value else: print(Fore.RED + "\nThis field cannot be left blank. Please try again." + Style.RESET_ALL) - time.sleep(2) - - -#! One time use until proper implementation -# Function to print the table of animals -def print_animal_table(animals): - - # Print table header - print("\nšŸ¾ " + Fore.CYAN + "List of Animals" + Style.RESET_ALL + " šŸ¾") - print("+-------------------------------------------------------------------------------------------+") - print("| " + Fore.YELLOW + "Name".ljust(20) + Style.RESET_ALL + "| " + Fore.YELLOW + "Species".ljust(8) + Style.RESET_ALL + "| " + Fore.YELLOW + "Breed".ljust(25) + Style.RESET_ALL + "| " + Fore.YELLOW + "Gender".ljust(15) + Style.RESET_ALL + "| " + Fore.YELLOW + "Age".ljust(1) + Style.RESET_ALL + " | " + Fore.YELLOW + "Adopted".ljust(7) + Style.RESET_ALL + " |") - print("+-------------------------------------------------------------------------------------------+") - - # Print each animal's data row by row - for animal in animals: - name_column = f"| {animal['name'].ljust(20)}" - species_column = f"| {animal['species'].ljust(8)}" - breed_column = f"| {animal['breed'].ljust(25)}" - gender_column = f"| {animal['gender'].ljust(15)}" - age_column = f"| {str(animal['age']).ljust(3)}" - adopted_column = f"| {str(animal['adopted']).ljust(7)} |" - print(name_column + species_column + breed_column + gender_column + age_column + adopted_column) - - # Print table footer - print("+-------------------------------------------------------------------------------------------+") \ No newline at end of file + time.sleep(2) \ No newline at end of file diff --git a/customer_adoption_form_dog.py b/customer_adoption_form_dog.py index 0f5864a..c66caab 100644 --- a/customer_adoption_form_dog.py +++ b/customer_adoption_form_dog.py @@ -3,13 +3,20 @@ from common_functions import clear_screen, get_mongodb_uri, get_input from pymongo import MongoClient +NON_VALID_INPUT = "Please enter a valid option." + # Connect to MongoDB uri = get_mongodb_uri() client = MongoClient(uri) db = client['animal_rescue'] customers_collection = db['customers'] - + +def section_clear(): + print(Fore.GREEN + "\nSection Completed." + Style.RESET_ALL) + time.sleep(2) + clear_screen() + def adopt_dog_form(): # Input validation loop while True: @@ -32,7 +39,7 @@ def adopt_dog_form(): customer_title = get_input("\nTitle (Mr/Mrs/Ms/Dr): ").strip() # Check title if customer_title not in ['Mr', 'Mrs', 'Ms', 'Dr']: - print(Fore.RED + "\nPlease enter a valid title." + Style.RESET_ALL) + print(Fore.RED + f"\n{NON_VALID_INPUT}" + Style.RESET_ALL) time.sleep(2) clear_screen() continue @@ -40,14 +47,14 @@ def adopt_dog_form(): customer_email = get_input("Your email address: ").strip() # Check email format if '@' not in customer_email or '.' not in customer_email: - print(Fore.RED + "\nPlease enter a valid email address." + Style.RESET_ALL) + print(Fore.RED + f"\n{NON_VALID_INPUT}" + Style.RESET_ALL) time.sleep(2) clear_screen() continue customer_phone = get_input("Your phone number: ").strip() # Check phone number format if not customer_phone.isdigit() or len(customer_phone) < 10: - print(Fore.RED + "\nPlease enter a valid phone number." + Style.RESET_ALL) + print(Fore.RED + f"\n{NON_VALID_INPUT}" + Style.RESET_ALL) time.sleep(2) clear_screen() continue @@ -57,7 +64,7 @@ def adopt_dog_form(): chosen_animal = get_input("\nDo you know which animal are you interested in adopting? ").strip() if chosen_animal not in ['yes', 'no']: - print(Fore.RED + "\nPlease enter a valid option." + Style.RESET_ALL) + print(Fore.RED + f"\n{NON_VALID_INPUT}" + Style.RESET_ALL) time.sleep(2) clear_screen() continue @@ -70,15 +77,13 @@ def adopt_dog_form(): elif chosen_animal == 'no': chosen_animal = "N/A" - print(Fore.GREEN + "\nSection Completed." + Style.RESET_ALL) - time.sleep(2) - clear_screen() + section_clear() print(Fore.LIGHTMAGENTA_EX + "\nHousing Details" + Style.RESET_ALL) own_rent = get_input("\nDo you own or rent your home? ").strip() if own_rent not in ['own', 'rent']: - print(Fore.RED + "\nPlease enter a valid option." + Style.RESET_ALL) + print(Fore.RED + f"\n{NON_VALID_INPUT}" + Style.RESET_ALL) time.sleep(2) clear_screen() continue @@ -88,7 +93,7 @@ def adopt_dog_form(): garden = get_input("\nDo you have a garden? (yes/no/communal): ").strip().lower() if garden not in ['yes', 'no', 'communal']: - print(Fore.RED + "\nPlease enter a valid option." + Style.RESET_ALL) + print(Fore.RED + f"\n{NON_VALID_INPUT}" + Style.RESET_ALL) time.sleep(2) clear_screen() continue @@ -98,19 +103,12 @@ def adopt_dog_form(): garden_fenced = get_input("Is your garden fully fenced? (yes/no): " + Style.RESET_ALL).strip().lower() garden_access = get_input("How is the garden accessed? " + Style.RESET_ALL).strip() - elif garden == "communal": - garden_size = "N/A" - garden_fenced = "N/A" - garden_access = "N/A" - - elif garden == "no": + elif garden == "communal" or garden == "no": garden_size = "N/A" garden_fenced = "N/A" garden_access = "N/A" - print(Fore.GREEN + "\nSection Completed." + Style.RESET_ALL) - time.sleep(2) - clear_screen() + section_clear() print(Fore.LIGHTMAGENTA_EX + "\nHousehold Details" + Style.RESET_ALL) @@ -119,7 +117,7 @@ def adopt_dog_form(): children_at_address = get_input("Do you have children living at your address? (yes/no): ").strip().lower() if children_at_address not in ['yes', 'no']: - print(Fore.RED + "\nPlease enter a valid option." + Style.RESET_ALL) + print(Fore.RED + f"\n{NON_VALID_INPUT}" + Style.RESET_ALL) time.sleep(2) clear_screen() continue @@ -133,7 +131,7 @@ def adopt_dog_form(): visiting_children = get_input("Do children visit your home? (yes/no): ").strip().lower() if visiting_children not in ['yes', 'no']: - print(Fore.RED + "\nPlease enter a valid option." + Style.RESET_ALL) + print(Fore.RED + f"\n{NON_VALID_INPUT}" + Style.RESET_ALL) time.sleep(2) clear_screen() continue @@ -144,16 +142,14 @@ def adopt_dog_form(): elif visiting_children == 'no': visiting_children_age = "N/A" - print(Fore.GREEN + "\nSection Completed." + Style.RESET_ALL) - time.sleep(2) - clear_screen() + section_clear() print(Fore.LIGHTMAGENTA_EX + "\nOther Pets" + Style.RESET_ALL) other_dogs = get_input("\nDo you have any other dogs? (yes/no): ").strip().lower() if other_dogs not in ['yes', 'no']: - print(Fore.RED + "\nPlease enter a valid option." + Style.RESET_ALL) + print(Fore.RED + f"\n{NON_VALID_INPUT}" + Style.RESET_ALL) time.sleep(2) clear_screen() continue @@ -175,7 +171,7 @@ def adopt_dog_form(): other_animals = get_input("Do you have any other animals? (yes/no): ").strip().lower() if other_animals not in ['yes', 'no']: - print(Fore.RED + "\nPlease enter a valid option." + Style.RESET_ALL) + print(Fore.RED + f"\n{NON_VALID_INPUT}" + Style.RESET_ALL) time.sleep(2) clear_screen() continue @@ -186,9 +182,7 @@ def adopt_dog_form(): elif other_animals == 'yes': other_animals_type = get_input(Fore.YELLOW + "What type of animals do you have? " + Style.RESET_ALL).strip() - print(Fore.GREEN + "\nSection Completed." + Style.RESET_ALL) - time.sleep(2) - clear_screen() + section_clear() print(Fore.LIGHTMAGENTA_EX + "\nExercise and Routine" + Style.RESET_ALL) weekday_exercise = get_input("\nHow often will the dog be exercised on weekdays? ").strip() @@ -197,15 +191,13 @@ def adopt_dog_form(): how_often_alone = get_input("How often will the dog be left alone? ").strip() alone_time = get_input("How long will the dog be left alone each day? ").strip() - print(Fore.GREEN + "Section Completed." + Style.RESET_ALL) - time.sleep(2) - clear_screen() + section_clear() print(Fore.LIGHTMAGENTA_EX + "\nFuture Plans" + Style.RESET_ALL) moving_plans = get_input("\nDo you have any plans to move in the next 6 months? (yes/no): ").strip().lower() if moving_plans not in ['yes', 'no']: - print(Fore.RED + "\nPlease enter a valid option." + Style.RESET_ALL) + print(Fore.RED + f"\n{NON_VALID_INPUT}" + Style.RESET_ALL) time.sleep(2) clear_screen() continue @@ -219,7 +211,7 @@ def adopt_dog_form(): holiday_plans = get_input("Do you have any holiday plans in the near future? (yes/no): ").strip().lower() if holiday_plans not in ['yes', 'no']: - print(Fore.RED + "\nPlease enter a valid option." + Style.RESET_ALL) + print(Fore.RED + f"\n{NON_VALID_INPUT}" + Style.RESET_ALL) time.sleep(2) clear_screen() continue @@ -230,9 +222,7 @@ def adopt_dog_form(): elif holiday_plans == 'no': holiday_timeframe = "N/A" - print(Fore.GREEN + "Section Completed." + Style.RESET_ALL) - time.sleep(2) - clear_screen() + section_clear() print(Fore.LIGHTMAGENTA_EX + "\nIdeal Dog" + Style.RESET_ALL) print(Fore.GREEN + "\nWhat type of dog are you looking for? " + Style.RESET_ALL) @@ -256,9 +246,7 @@ def adopt_dog_form(): ideal_dog_training = get_input("Training Needs: ").strip() ideal_dog_health = get_input("Health Needs: ").strip() - print(Fore.GREEN + "\nSection Completed." + Style.RESET_ALL) - time.sleep(2) - clear_screen() + section_clear() print(Fore.LIGHTMAGENTA_EX + "\nOther Requirements" + Style.RESET_ALL) @@ -275,7 +263,7 @@ def adopt_dog_form(): other_requirements = get_input(Fore.YELLOW + "\nDo you have any other requirements? (yes/no) " + Style.RESET_ALL).strip() if other_requirements not in ['yes', 'no']: - print(Fore.RED + "\nPlease enter a valid option." + Style.RESET_ALL) + print(Fore.RED + f"\n{NON_VALID_INPUT}" + Style.RESET_ALL) time.sleep(2) clear_screen() continue @@ -289,7 +277,7 @@ def adopt_dog_form(): experience = get_input("\nDo you have any experience with dogs? (yes/no) ").strip() if experience not in ['yes', 'no']: - print(Fore.RED + "\nPlease enter a valid option." + Style.RESET_ALL) + print(Fore.RED + f"\n{NON_VALID_INPUT}" + Style.RESET_ALL) time.sleep(2) clear_screen() continue @@ -300,9 +288,7 @@ def adopt_dog_form(): elif experience == 'no': experience = "N/A" - print(Fore.GREEN + "\nSection Completed." + Style.RESET_ALL) - time.sleep(2) - clear_screen() + section_clear() print(Fore.LIGHTMAGENTA_EX + "\nConsent to Terms" + Style.RESET_ALL) signature = get_input("\nPlease enter your full name as a signature: ").strip() diff --git a/edit_animal_entries.py b/edit_animal_entries.py index c7965ac..4032137 100644 --- a/edit_animal_entries.py +++ b/edit_animal_entries.py @@ -1,5 +1,6 @@ import time -from common_functions import clear_screen, get_mongodb_uri, print_animal_table, load_animal_data +from common_functions import clear_screen, get_mongodb_uri, load_animal_data +from tables import print_animal_table from colorama import Fore, Style from pymongo import MongoClient from sudo_user import sudo_user @@ -11,17 +12,44 @@ db = client['animal_rescue'] animals_collection = db['animals'] +FIELDS = { + 1: "name", + 2: "species", + 3: "breed", + 4: "gender", + 5: "age" +} + +def get_animal_name(): + return input(Fore.CYAN + "Enter the name of the animal to modify (enter 'exit' to leave): " + Style.RESET_ALL).strip().capitalize() + +def get_field_choice(): + return input("Enter the number of the field to modify or 'exit' to cancel: ") + +def get_new_value(field): + return input(f"Enter new {field}: ").strip().capitalize() + +def update_animal_field(animal, field, new_value): + if new_value != animal[field]: + animals_collection.update_one({'name': animal['name']}, {'$set': {field: new_value}}) + print(Fore.GREEN + f"\n{field.capitalize()} updated successfully." + Style.RESET_ALL) + time.sleep(2) + clear_screen() + print_animal_table(load_animal_data(animals_collection)) + else: + print(Fore.YELLOW + f"\nNew {field} is the same as the current one. No update performed." + Style.RESET_ALL) + time.sleep(2) + clear_screen() + print_animal_table(load_animal_data(animals_collection)) def modify_animal(): - animals = load_animal_data(animals_collection) clear_screen() sudo_user() print(Fore.CYAN + "\nšŸ¾ Modify Animal šŸ¾\n" + Style.RESET_ALL) - # Input the name of the animal to modify - animal_name = input(Fore.CYAN + "Enter the name of the animal to modify (enter 'exit' to leave): " + Style.RESET_ALL).strip().capitalize() + animal_name = get_animal_name() if animal_name.lower() == 'exit': print(Fore.YELLOW + "\nExiting..." + Style.RESET_ALL) @@ -29,8 +57,7 @@ def modify_animal(): clear_screen() print_animal_table(animals) return - - # Search for the animal in the database + animal = animals_collection.find_one({'name': animal_name}) if animal: @@ -42,7 +69,7 @@ def modify_animal(): print("5. Age") print(Style.RESET_ALL) - field_choice = input("Enter the number of the field to modify or 'exit' to cancel: ") + field_choice = get_field_choice() if field_choice.lower() == 'exit': print(Fore.YELLOW + "\nExiting..." + Style.RESET_ALL) @@ -51,52 +78,11 @@ def modify_animal(): if field_choice.isdigit(): field_choice = int(field_choice) - if field_choice == 1: - new_value = input("Enter new name: ").strip() - if new_value != animal['name']: - animals_collection.update_one({'name': animal_name}, {'$set': {'name': new_value}}) - print(Fore.GREEN + "\nName updated successfully." + Style.RESET_ALL) - else: - print(Fore.YELLOW + "\nNew name is the same as the current one. No update performed." + Style.RESET_ALL) - elif field_choice == 2: - new_value = input("Enter new species: ").strip() - if new_value != animal['species']: - animals_collection.update_one({'name': animal_name}, {'$set': {'species': new_value}}) - print(Fore.GREEN + "\nSpecies updated successfully." + Style.RESET_ALL) - else: - print(Fore.YELLOW + "\nNew species is the same as the current one. No update performed." + Style.RESET_ALL) - elif field_choice == 3: - new_value = input("Enter new breed: ").strip() - if new_value != animal['breed']: - animals_collection.update_one({'name': animal_name}, {'$set': {'breed': new_value}}) - print(Fore.GREEN + "\nBreed updated successfully." + Style.RESET_ALL) - else: - print(Fore.YELLOW + "\nNew breed is the same as the current one. No update performed." + Style.RESET_ALL) - elif field_choice == 4: - new_value = input("Enter new gender: ").strip().lower() - if new_value in ['male', 'female'] and new_value != animal['gender']: - animals_collection.update_one({'name': animal_name}, {'$set': {'gender': new_value}}) - print(Fore.GREEN + "\nGender updated successfully." + Style.RESET_ALL) - elif new_value not in ['male', 'female']: - print(Fore.RED + "\nInvalid input. Gender must be 'Male' or 'Female'." + Style.RESET_ALL) - else: - print(Fore.YELLOW + "\nNew gender is the same as the current one. No update performed." + Style.RESET_ALL) - elif field_choice == 5: - new_value = input("Enter new age: ").strip() - if new_value.isdigit() and int(new_value) > 0 and int(new_value) != animal['age']: - animals_collection.update_one({'name': animal_name}, {'$set': {'age': int(new_value)}}) - print(Fore.GREEN + "\nAge updated successfully." + Style.RESET_ALL) - elif not new_value.isdigit() or int(new_value) <= 0: - print(Fore.RED + "Invalid age. Please enter a positive integer." + Style.RESET_ALL) - else: - print(Fore.YELLOW + "\nNew age is the same as the current one. No update performed." + Style.RESET_ALL) + if field_choice in FIELDS: + field = FIELDS[field_choice] + new_value = get_new_value(field) + update_animal_field(animal, field, new_value) else: - print(Fore.RED + "Invalid choice." + Style.RESET_ALL) - - else: - print(Fore.RED + "Animal not found." + Style.RESET_ALL) - time.sleep(2) - clear_screen() - modify_animal() - - input("Press Enter to continue...") \ No newline at end of file + print(Fore.RED + "Invalid input! Please enter a valid number." + Style.RESET_ALL) + time.sleep(2) + modify_animal() \ No newline at end of file diff --git a/login.py b/login.py index d3fe00e..4242ca9 100644 --- a/login.py +++ b/login.py @@ -1,7 +1,7 @@ import getpass import time from colorama import Fore, Style -from common_functions import clear_screen, log_action, hash_password, verify_password, get_mongodb_uri +from common_functions import clear_screen, log_action, hash_password, verify_password, get_mongodb_uri, get_input from admin_dashboard import admin_dashboard from pymongo import MongoClient @@ -46,14 +46,14 @@ def change_admin_password(username): {'$set': {'hashed_password': hashed_password}} ) - log_action(username, f"Admin Password has been changed") + log_action("ADMIN", "ADMIN Password has been changed") print(Fore.GREEN + "\nPassword changed successfully!" + Style.RESET_ALL) time.sleep(2) clear_screen() else: # Notify the user about mismatching passwords and prompt again print(Fore.RED + "\nPasswords do not match. Please try again." + Style.RESET_ALL) - log_action("Failed attempt to access ADMIN") + log_action(username, "Failed attempt to access ADMIN") time.sleep(2) clear_screen() change_admin_password(username) @@ -62,7 +62,7 @@ def reset_password(username): clear_screen() # Notify the user about changing the password - print(Fore.YELLOW + f"\nYour password must be changed for security reasons." + Style.RESET_ALL) + print(Fore.YELLOW + "\nYour password must be changed for security reasons." + Style.RESET_ALL) # Prompt user for a new password and confirmation while True: @@ -104,59 +104,57 @@ def login(): # Display login prompt print("\nšŸ‘¤ User Login šŸ‘¤") username = input("\nEnter your username: ") - password = getpass.getpass("Enter your password: ") # No need for getpass as input is hidden in most consoles + password = getpass.getpass("Enter your password: ") # Query user credentials from MongoDB user = users_collection.find_one({'username': username}) - if user: - stored_password = user['hashed_password'] - - # Verify the entered password with the stored password hash - if verify_password(stored_password, password): - user_level = user['level'] - - if username == "ADMIN" and password == "ADMIN": - print("\nLogging In...") - time.sleep(2) - change_admin_password(username) - admin_dashboard() - return username, user_level - - elif username == "ADMIN": - log_action(username, f"ADMIN Logged In") - print("\nLogging In...") - time.sleep(2) - admin_dashboard() - return username, user_level - - elif password == "password": - print("\nLogging In...") - # Pass username to the reset_password function - reset_password(username) - - else: - print("\nLogging in...") - time.sleep(2) - log_action(username, f"Logged In") - return username, user_level - - else: - print(Fore.RED + "\nIncorrect password." + Style.RESET_ALL) - attempts += 1 - time.sleep(2) - print(Fore.RED + f"\nRemaining attempts: {MAX_ATTEMPTS - attempts}" + Style.RESET_ALL) - log_action(username, "Failed login attempt") - time.sleep(2) - clear_screen() + if user and verify_password(user['hashed_password'], password): + return handle_successful_login(user, username, password) + else: + handle_failed_login(username, password, attempts) + attempts += 1 + + # If user exceeds maximum login attempts + print(Fore.RED + "\nYou have exceeded the maximum number of login attempts." + Style.RESET_ALL) + time.sleep(1) + log_action(username, "Failed to login") + print("Exiting...") + time.sleep(2) + exit() + +def get_user_credentials(): + print("\nšŸ‘¤ User Login šŸ‘¤") + username = input("\nEnter your username: ") + password = getpass.getpass("Enter your password: ") + return username, password + +def handle_successful_login(user, username, password): + user_level = user['level'] + print("\nLogging in...") + time.sleep(2) + if username == "ADMIN": + if password == "ADMIN": + change_admin_password(username) + admin_dashboard() else: - print(Fore.RED + "\nUsername not found. Please try again." + Style.RESET_ALL) - time.sleep(2) - clear_screen() + admin_dashboard() + elif password == "password": + reset_password(username) + else: + log_action(username, "Logged in") - print(Fore.RED + "\nMaximum login attempts reached" + Style.RESET_ALL) - time.sleep(1) - print("\nExiting...") + return username, user_level + +def handle_failed_login(user, username, attempts): + if user: + print(Fore.RED + "\nInvalid password. Please try again." + Style.RESET_ALL) + log_action(username, "Failed to login") + else: + print(Fore.RED + "\nUser not found. Please try again." + Style.RESET_ALL) + log_action(username, "Failed to be found") + time.sleep(2) - exit() \ No newline at end of file + print(Fore.RED + f"\nYou have {MAX_ATTEMPTS - attempts} attempts remaining." + Style.RESET_ALL) + clear_screen() \ No newline at end of file diff --git a/register.py b/register.py index 47dd074..5b2807d 100644 --- a/register.py +++ b/register.py @@ -14,65 +14,64 @@ def register(): clear_screen() while True: - # Prompt user to enter a username - username = input("\nEnter a username: ").strip() + username = get_username() + if username is None: + continue - if username == "": - clear_screen() - print(Fore.RED + "\nUsername cannot be empty. Please try again." + Style.RESET_ALL) + password = get_password("Enter a password: ") + if password is None: continue - - # Check if username already exists in the database - if users_collection.find_one({'username': username}): - print(Fore.RED + "\nUsername already exists. Please choose another one." + Style.RESET_ALL) - time.sleep(2) - clear_screen() + + confirm_password = get_password("Confirm your password: ") + if confirm_password is None: continue - # Continuous loop for user registration - while True: - # Prompt user to enter and confirm a password - while True: - password = getpass.getpass("Enter a password: ") - if not password.strip(): - clear_screen() - print(Fore.RED + "\nPassword cannot be empty. Please try again." + Style.RESET_ALL) - continue - else: - break - - while True: - confirm_password = getpass.getpass("Confirm your password: ") - if not confirm_password.strip(): - clear_screen() - print(Fore.RED + "\nPassword cannot be empty. Please try again." + Style.RESET_ALL) - continue - else: - break + if password != confirm_password: + print_error_message("\nPasswords do not match. Please try again.") + continue + + user_level = get_user_level() + if user_level is None: + continue - # Check if passwords match - if password == confirm_password: - # Prompt user to enter their user level - user_level = input("Enter your user level (1-3): ") - - # Validate user level input - if user_level.isdigit() and 1 <= int(user_level) <= 3: - # Hash the password - hashed_password = hash_password(password) + hashed_password = hash_password(password) + users_collection.insert_one({ + 'username': username, + 'hashed_password': hashed_password, + 'level': int(user_level) + }) - # Insert user data into the MongoDB collection - users_collection.insert_one({ - 'username': username, - 'hashed_password': hashed_password, - 'level': int(user_level) - }) + print(Fore.GREEN + "\nRegistration successful!" + Style.RESET_ALL) + time.sleep(2) + clear_screen() + return - print(Fore.GREEN + "\nRegistration successful!" + Style.RESET_ALL) - time.sleep(2) - clear_screen() - return - else: - print(Fore.RED + "\nInvalid user level. Please enter a number between 1 and 3." + Style.RESET_ALL) - else: - clear_screen() - print(Fore.RED + "\nPasswords do not match. Please try again." + Style.RESET_ALL) \ No newline at end of file +def get_username(): + username = input("\nEnter a username: ").strip() + if username == "": + print_error_message("\nUsername cannot be empty. Please try again.") + return None + if users_collection.find_one({'username': username}): + print_error_message("\nUsername already exists. Please choose another one.") + time.sleep(2) + clear_screen() + return None + return username + +def get_password(prompt): + password = getpass.getpass(prompt) + if not password.strip(): + print_error_message("\nPassword cannot be empty. Please try again.") + return None + return password + +def get_user_level(): + user_level = input("Enter your user level (1-3): ") + if not user_level.isdigit() or not 1 <= int(user_level) <= 3: + print_error_message("\nInvalid user level. Please enter a number between 1 and 3.") + return None + return user_level + +def print_error_message(message): + clear_screen() + print(Fore.RED + message + Style.RESET_ALL) \ No newline at end of file diff --git a/sudo_admin.py b/sudo_admin.py index c158920..ef6cc45 100644 --- a/sudo_admin.py +++ b/sudo_admin.py @@ -13,21 +13,15 @@ users_collection = db['users'] def sudo_admin(): - clear_screen() - # Continuous loop for sudo user authentication attempts = 0 while attempts < MAX_ATTEMPTS: print(Fore.LIGHTMAGENTA_EX + "\nšŸ‘¤ ADMIN Sudo Login šŸ‘¤" + Style.RESET_ALL) print("\nPlease enter your credentials") - username = input("\nEnter your username: ") - password = getpass.getpass("Enter your password: ") - if not username or not password: - print(Fore.RED + "\nUsername and password are required." + Style.RESET_ALL) - time.sleep(2) - clear_screen() + username, password = get_credentials() + if username is None or password is None: continue user = users_collection.find_one({'username': username}) @@ -35,45 +29,60 @@ def sudo_admin(): if user: stored_password = user['hashed_password'] - # Verify the entered password with the stored password hash if verify_password(stored_password, password): - # Throw error if user is logging in with ADMIN account if username == "ADMIN": print(Fore.GREEN +"\nUser Verified..." + Style.RESET_ALL) time.sleep(2) clear_screen() return username - # Notify insufficient clearance else: - print(Fore.RED + "\nYou do not have clearance to do this." + Style.RESET_ALL) - time.sleep(1) - print(Fore.RED + "\nADMIN user will be alerted." + Style.RESET_ALL) - time.sleep(1) - print(Fore.RED + "\nExiting..." + Style.RESET_ALL) - time.sleep(1) - - # Log user's username and add it to audit log - log_action(username, f"Tried to access without clearance") + print_insufficient_clearance(username) exit() - - # Notify about incorrect password else: - print(Fore.RED + "\nIncorrect password." + Style.RESET_ALL) + print_incorrect_password(username, attempts) attempts += 1 - time.sleep(2) - print(Fore.RED + f"\nRemaining attempts: {MAX_ATTEMPTS - attempts}" + Style.RESET_ALL) - log_action(username, "Failed attempted access via sudo") - time.sleep(2) - clear_screen() - - # Notify about non-existing username else: - print(Fore.RED + "\nUsername not found. Please try again." + Style.RESET_ALL) - time.sleep(2) - clear_screen() + print_username_not_found() + print_max_attempts_reached() + +def get_credentials(): + username = input("\nEnter your username: ") + password = getpass.getpass("Enter your password: ") + + if not username or not password: + print(Fore.RED + "\nUsername and password are required." + Style.RESET_ALL) + time.sleep(2) + clear_screen() + return None, None + + return username, password + +def print_insufficient_clearance(username): + print(Fore.RED + "\nYou do not have clearance to do this." + Style.RESET_ALL) + time.sleep(1) + print(Fore.RED + "\nADMIN user will be alerted." + Style.RESET_ALL) + time.sleep(1) + print(Fore.RED + "\nExiting..." + Style.RESET_ALL) + time.sleep(1) + log_action(username, "Tried to access without clearance") + +def print_incorrect_password(username, attempts): + print(Fore.RED + "\nIncorrect password." + Style.RESET_ALL) + time.sleep(2) + print(Fore.RED + f"\nRemaining attempts: {MAX_ATTEMPTS - attempts}" + Style.RESET_ALL) + log_action(username, "Failed attempted access via sudo") + time.sleep(2) + clear_screen() + +def print_username_not_found(): + print(Fore.RED + "\nUsername not found. Please try again." + Style.RESET_ALL) + time.sleep(2) + clear_screen() + +def print_max_attempts_reached(): print(Fore.RED + "\nMaximum login attempts reached" + Style.RESET_ALL) time.sleep(1) print("\nExiting...") time.sleep(2) - exit() \ No newline at end of file + exit() \ No newline at end of file diff --git a/sudo_user.py b/sudo_user.py index 7f6c654..deab430 100644 --- a/sudo_user.py +++ b/sudo_user.py @@ -13,80 +13,80 @@ users_collection = db['users'] def sudo_user(): - clear_screen() - - # Continuous loop for sudo user authentication attempts = 0 + while attempts < MAX_ATTEMPTS: print(Fore.LIGHTMAGENTA_EX + "\nšŸ‘¤ Sudo Login šŸ‘¤" + Style.RESET_ALL) print("\nPlease enter your credentials") - username = input("\nEnter your username: ") - password = getpass.getpass("Enter your password: ") - if not username or not password: - print(Fore.RED + "\nUsername and password are required." + Style.RESET_ALL) - time.sleep(2) - clear_screen() + username, password = get_credentials() + if username is None or password is None: continue user = users_collection.find_one({'username': username}) if user: stored_password = user['hashed_password'] - - # Verify the entered password with the stored password hash + if verify_password(stored_password, password): user_level = user['level'] - - # Throw error if user is logging in with ADMIN account + if username == "ADMIN": print("\nNot a valid username") clear_screen() return username - - elif username == None: - print("\nNot a valid username") - clear_screen() - sudo_user - return username - - # Notify about user verification for high level users elif user_level >= 2: print(Fore.GREEN +"\nUser Verified..." + Style.RESET_ALL) time.sleep(2) clear_screen() return username - - # Notify insufficient clearance else: - print(Fore.RED + "\nYou do not have clearance to do this." + Style.RESET_ALL) - time.sleep(1) - print(Fore.RED + "\nADMIN user will be alerted." + Style.RESET_ALL) - time.sleep(1) - print(Fore.RED + "\nExiting..." + Style.RESET_ALL) - time.sleep(1) - - # Log user's username and add it to audit log - log_action(username, f"Tried to access without clearance") + print_insufficient_clearance(username) exit() - - # Notify about incorrect password + else: + print_incorrect_password(username, attempts) + attempts += 1 else: - print(Fore.RED + "\nIncorrect password." + Style.RESET_ALL) - attempts += 1 - time.sleep(2) - print(Fore.RED + f"\nRemaining attempts: {MAX_ATTEMPTS - attempts}" + Style.RESET_ALL) - log_action(username, "Failed attempted access via sudo") - time.sleep(2) - clear_screen() - - # Notify about non-existing username - else: - print(Fore.RED + "\nUsername not found. Please try again." + Style.RESET_ALL) + print_username_not_found() + + print_max_attempts_reached() + +def get_credentials(): + username = input("\nEnter your username: ") + password = getpass.getpass("Enter your password: ") + + if not username or not password: + print(Fore.RED + "\nUsername and password are required." + Style.RESET_ALL) time.sleep(2) clear_screen() - + return None, None + + return username, password + +def print_insufficient_clearance(username): + print(Fore.RED + "\nYou do not have clearance to do this." + Style.RESET_ALL) + time.sleep(1) + print(Fore.RED + "\nADMIN user will be alerted." + Style.RESET_ALL) + time.sleep(1) + print(Fore.RED + "\nExiting..." + Style.RESET_ALL) + time.sleep(1) + log_action(username, "Tried to access without clearance") + +def print_incorrect_password(username, attempts): + print(Fore.RED + "\nIncorrect password." + Style.RESET_ALL) + time.sleep(2) + print(Fore.RED + f"\nRemaining attempts: {MAX_ATTEMPTS - attempts}" + Style.RESET_ALL) + log_action(username, "Failed attempted access via sudo") + time.sleep(2) + clear_screen() + +def print_username_not_found(): + print(Fore.RED + "\nUsername not found. Please try again." + Style.RESET_ALL) + time.sleep(2) + clear_screen() + +def print_max_attempts_reached(): print(Fore.RED + "\nMaximum login attempts reached" + Style.RESET_ALL) time.sleep(1) print("\nExiting...") diff --git a/sudo_user_level_1.py b/sudo_user_level_1.py index d21bcf3..75fab47 100644 --- a/sudo_user_level_1.py +++ b/sudo_user_level_1.py @@ -13,77 +13,81 @@ users_collection = db['users'] def sudo_user(): - # Continuous loop for sudo user authentication - attempts = 0 - clear_screen() - + attempts = 0 while attempts < MAX_ATTEMPTS: print(Fore.LIGHTMAGENTA_EX + "\nšŸ‘¤ Sudo Login šŸ‘¤" + Style.RESET_ALL) print("\nPlease enter your credentials") - username = input("\nEnter your username: ") - password = getpass.getpass("Enter your password: ") - if not username or not password: - print(Fore.RED + "\nUsername and password are required." + Style.RESET_ALL) - time.sleep(2) - clear_screen() + username, password = get_credentials() + if username is None or password is None: continue user = users_collection.find_one({'username': username}) if user: stored_password = user['hashed_password'] - - # Verify the entered password with the stored password hash + if verify_password(stored_password, password): user_level = user['level'] - # Throw error if user is logging in with ADMIN account if username == "ADMIN": print("\nNot a valid username") clear_screen() sudo_user() return username - - # Notify about user verification elif user_level >= 1: print(Fore.GREEN +"\nUser Verified..." + Style.RESET_ALL) time.sleep(2) clear_screen() return user - - # Notify insufficient clearance else: - print(Fore.RED + "\nYou do not have clearance to do this." + Style.RESET_ALL) - time.sleep(1) - print(Fore.RED + "\nADMIN user will be alerted." + Style.RESET_ALL) - time.sleep(1) - print(Fore.RED + "\nExiting..." + Style.RESET_ALL) - time.sleep(1) - - # Log user's username and add it to audit log - log_action(username, f"Tried to access without clearance") + print_insufficient_clearance(username) exit() - - # Notify about incorrect password else: - print(Fore.RED + "\nIncorrect password." + Style.RESET_ALL) + print_incorrect_password(username, attempts) attempts += 1 - time.sleep(2) - print(Fore.RED + f"\nRemaining attempts: {MAX_ATTEMPTS - attempts}" + Style.RESET_ALL) - log_action(username, "Failed attempted access via sudo") - time.sleep(2) - clear_screen() - - # Notify about non-existing username else: - print(Fore.RED + "\nUsername not found. Please try again." + Style.RESET_ALL) - time.sleep(2) - clear_screen() + print_username_not_found() - + print_max_attempts_reached() + +def get_credentials(): + username = input("\nEnter your username: ") + password = getpass.getpass("Enter your password: ") + + if not username or not password: + print(Fore.RED + "\nUsername and password are required." + Style.RESET_ALL) + time.sleep(2) + clear_screen() + return None, None + + return username, password + +def print_insufficient_clearance(username): + print(Fore.RED + "\nYou do not have clearance to do this." + Style.RESET_ALL) + time.sleep(1) + print(Fore.RED + "\nADMIN user will be alerted." + Style.RESET_ALL) + time.sleep(1) + print(Fore.RED + "\nExiting..." + Style.RESET_ALL) + time.sleep(1) + log_action(username, "Tried to access without clearance") + +def print_incorrect_password(username, attempts): + print(Fore.RED + "\nIncorrect password." + Style.RESET_ALL) + time.sleep(2) + print(Fore.RED + f"\nRemaining attempts: {MAX_ATTEMPTS - attempts}" + Style.RESET_ALL) + log_action(username, "Failed attempted access via sudo") + time.sleep(2) + clear_screen() + +def print_username_not_found(): + print(Fore.RED + "\nUsername not found. Please try again." + Style.RESET_ALL) + time.sleep(2) + clear_screen() + +def print_max_attempts_reached(): print(Fore.RED + "\nMaximum login attempts reached" + Style.RESET_ALL) time.sleep(1) print("\nExiting...") diff --git a/tables.py b/tables.py new file mode 100644 index 0000000..b71e95f --- /dev/null +++ b/tables.py @@ -0,0 +1,41 @@ +from colorama import Fore, Style +from common_functions import clear_screen + +basic_animal_table = ("+-------------------------------------------------------------------------------------------+") +animal_table_with_index = ("+---------------------------------------------------------------------------------+") + +# Function to print the table of animals +def print_animal_table(animals): + + # Print table header + print("\nšŸ¾ " + Fore.CYAN + "List of Animals" + Style.RESET_ALL + " šŸ¾") + print(basic_animal_table) + print("| " + Fore.YELLOW + "Name".ljust(20) + Style.RESET_ALL + "| " + Fore.YELLOW + "Species".ljust(8) + Style.RESET_ALL + "| " + Fore.YELLOW + "Breed".ljust(25) + Style.RESET_ALL + "| " + Fore.YELLOW + "Gender".ljust(15) + Style.RESET_ALL + "| " + Fore.YELLOW + "Age".ljust(1) + Style.RESET_ALL + " | " + Fore.YELLOW + "Adopted".ljust(7) + Style.RESET_ALL + " |") + print(basic_animal_table) + + # Print each animal's data row by row + for animal in animals: + name_column = f"| {animal['name'].ljust(20)}" + species_column = f"| {animal['species'].ljust(8)}" + breed_column = f"| {animal['breed'].ljust(25)}" + gender_column = f"| {animal['gender'].ljust(15)}" + age_column = f"| {str(animal['age']).ljust(3)}" + adopted_column = f"| {str(animal['adopted']).ljust(7)} |" + print(name_column + species_column + breed_column + gender_column + age_column + adopted_column) + + # Print table footer + print(basic_animal_table) + +# Function to print the table of animals with index numbers +def print_animal_table_with_index(animals): + # Displays the table of animals with index numbers + clear_screen() + print("\nšŸ¾ " + Fore.CYAN + "List of Animals" + Style.RESET_ALL + " šŸ¾") + print(animal_table_with_index) + print("| " + Fore.YELLOW + "Index " + Style.RESET_ALL + "| " + Fore.YELLOW + "Name " + Style.RESET_ALL + "| " + Fore.YELLOW + "Species " + Style.RESET_ALL + "| " + Fore.YELLOW + "Breed " + Style.RESET_ALL + "| " + Fore.YELLOW + "Gender " + Style.RESET_ALL + "| " + Fore.YELLOW + "Age" + Style.RESET_ALL + " |") + print(animal_table_with_index) + + for i, animal in enumerate(animals, 1): + print(f"| {i} | {animal['name'].ljust(20)} | {animal['species'].ljust(7)} | {animal['breed'].ljust(20)} | {animal['gender'].ljust(6)} | {str(animal['age']).ljust(3)} |") + + print(animal_table_with_index) \ No newline at end of file diff --git a/user_management.py b/user_management.py index 6693ced..b4c8fab 100644 --- a/user_management.py +++ b/user_management.py @@ -59,60 +59,64 @@ def reset_user_password(username): print(Fore.RED + f"\nUser '{username}' not found." + Style.RESET_ALL) time.sleep(2) +def get_username(): + return input("\nEnter the username to update information: ") + +def get_user(username): + return users_collection.find_one({'username': username}) + +def print_user_info(username, user): + clear_screen() + print(Fore.CYAN + "\nCurrent user information:" + Style.RESET_ALL) + print(Fore.GREEN + f"\nUsername: {username}" + Style.RESET_ALL) + print(Fore.GREEN + f"User Level: {user['level']}" + Style.RESET_ALL) + +def get_option(): + print("\nSelect the information you want to update:") + print("\n1. Username") + print("2. User Level") + print("3. Reset Password") + print("4. Cancel") + return input("\nEnter your choice: ") + +def update_username(username): + new_username = input("Enter the new username: ") + if not users_collection.find_one({'username': new_username}): + users_collection.update_one({'username': username}, {'$set': {'username': new_username}}) + print(Fore.GREEN + f"\nUsername updated successfully to '{new_username}'!" + Style.RESET_ALL) + else: + print(Fore.RED + f"\nUsername '{new_username}' already exists. Please choose a different username." + Style.RESET_ALL) + +def update_user_level(username): + new_level = input("Enter the new user level: ") + if new_level.isdigit(): # Check if the input is a valid integer + new_level = int(new_level) + users_collection.update_one({'username': username}, {'$set': {'level': new_level}}) + print(Fore.GREEN + f"\nUser level updated successfully for '{username}'!" + Style.RESET_ALL) + else: + print(Fore.RED + "\nInvalid user level. Please enter a valid level." + Style.RESET_ALL) + def update_user_information(): - # Prompt for username to update information - username = input("\nEnter the username to update information: ") + username = get_username() + user = get_user(username) - user = users_collection.find_one({'username': username}) if user and username != "ADMIN": - clear_screen() - print(Fore.CYAN + "\nCurrent user information:" + Style.RESET_ALL) - print(Fore.GREEN + f"\nUsername: {username}" + Style.RESET_ALL) - print(Fore.GREEN + f"User Level: {user['level']}" + Style.RESET_ALL) - - # Prompt for the information to be updated - print("\nSelect the information you want to update:") - print("\n1. Username") - print("2. User Level") - print("3. Reset Password") - print("4. Cancel") + print_user_info(username, user) + option = get_option() - option = input("\nEnter your choice: ") - - # New Username if option == '1': - new_username = input("Enter the new username: ") - if not users_collection.find_one({'username': new_username}): - users_collection.update_one({'username': username}, {'$set': {'username': new_username}}) - print(Fore.GREEN + f"\nUsername updated successfully to '{new_username}'!" + Style.RESET_ALL) - else: - print(Fore.RED + f"\nUsername '{new_username}' already exists. Please choose a different username." + Style.RESET_ALL) - - # New User Level + update_username(username) elif option == '2': - new_level = input("Enter the new user level: ") - if new_level.isdigit(): # Check if the input is a valid integer - new_level = int(new_level) - users_collection.update_one({'username': username}, {'$set': {'level': new_level}}) - print(Fore.GREEN + f"\nUser level updated successfully for '{username}'!" + Style.RESET_ALL) - else: - print(Fore.RED + "\nInvalid user level. Please enter a valid level." + Style.RESET_ALL) - - # Reset Password + update_user_level(username) elif option == '3': time.sleep(2) reset_user_password(username) - - # Cancel elif option == '4': print("\nOperation canceled.") else: print(Fore.RED + "\nInvalid option. Please try again." + Style.RESET_ALL) - - # Throw error if user does not exist or is ADMIN elif username == "ADMIN": print(Fore.RED + "\nYou cannot update information for the ADMIN user." + Style.RESET_ALL) - else: print(Fore.RED + f"\nUser '{username}' not found." + Style.RESET_ALL) diff --git a/view_animal_profile.py b/view_animal_profile.py index 3e33c8f..90bf169 100644 --- a/view_animal_profile.py +++ b/view_animal_profile.py @@ -3,8 +3,9 @@ from PIL import Image, ImageTk from tkinter import filedialog from colorama import Fore, Style -from common_functions import clear_screen, load_animal_data, log_action, get_mongodb_uri, print_animal_table +from common_functions import clear_screen, log_action, get_mongodb_uri from sudo_user import sudo_user +from tables import print_animal_table_with_index from pymongo import MongoClient # Connect to MongoDB @@ -13,19 +14,8 @@ db = client['animal_rescue'] animals_collection = db['animals'] - -def print_animal_table_with_index(animals): - # Displays the table of animals with index numbers - clear_screen() - print("\nšŸ¾ " + Fore.CYAN + "List of Animals" + Style.RESET_ALL + " šŸ¾") - print("+---------------------------------------------------------------------------------+") - print("| " + Fore.YELLOW + "Index " + Style.RESET_ALL + "| " + Fore.YELLOW + "Name " + Style.RESET_ALL + "| " + Fore.YELLOW + "Species " + Style.RESET_ALL + "| " + Fore.YELLOW + "Breed " + Style.RESET_ALL + "| " + Fore.YELLOW + "Gender " + Style.RESET_ALL + "| " + Fore.YELLOW + "Age" + Style.RESET_ALL + " |") - print("+---------------------------------------------------------------------------------+") - - for i, animal in enumerate(animals, 1): - print(f"| {i} | {animal['name'].ljust(20)} | {animal['species'].ljust(7)} | {animal['breed'].ljust(20)} | {animal['gender'].ljust(6)} | {str(animal['age']).ljust(3)} |") - print("+---------------------------------------------------------------------------------+") +invalid_input = "Invalid input! Please enter a valid index." def search_animal_by_name(): # Asks the user for the name of the animal to search for @@ -47,7 +37,7 @@ def search_animal_by_name(): print("\nExiting search...") time.sleep(2) clear_screen() - view_animal_profile() + view_animal_profile(selected_index, selected_animal) try: selected_index = int(selected_index) @@ -55,13 +45,13 @@ def search_animal_by_name(): selected_animal = animals[selected_index - 1] view_animal_profile(selected_index, selected_animal) else: - print(Fore.RED + "Invalid input! Please enter a valid index." + Style.RESET_ALL) + print(Fore.RED + f"{invalid_input}" + Style.RESET_ALL) time.sleep(2) - view_animal_profile() + view_animal_profile(selected_index, selected_animal) except ValueError: - print(Fore.RED + "Invalid input! Please enter a valid index." + Style.RESET_ALL) + print(Fore.RED + f"{invalid_input}" + Style.RESET_ALL) time.sleep(2) - view_animal_profile() + view_animal_profile(selected_index, selected_animal) def view_animal_profile(selected_index, selected_animal): clear_screen() @@ -135,13 +125,13 @@ def upload_image(): root.mainloop() else: - print(Fore.RED + "Invalid input! Please enter a valid index." + Style.RESET_ALL) + print(Fore.RED + f"{invalid_input}" + Style.RESET_ALL) time.sleep(2) - view_animal_profile() + view_animal_profile(selected_index, selected_animal) except ValueError: - print(Fore.RED + "Invalid input! Please enter a valid index." + Style.RESET_ALL) + print(Fore.RED + f"{invalid_input}" + Style.RESET_ALL) time.sleep(2) - view_animal_profile() + view_animal_profile(selected_index, selected_animal) def view_animals_full(): clear_screen() diff --git a/view_animals.py b/view_animals.py index 927fcd0..f215fad 100644 --- a/view_animals.py +++ b/view_animals.py @@ -5,6 +5,7 @@ from sudo_user_level_1 import sudo_user from edit_animal_entries import modify_animal from add_animal import add_animal +from tables import print_animal_table from pymongo import MongoClient # Connect to MongoDB @@ -14,28 +15,8 @@ db = client['animal_rescue'] animals_collection = db['animals'] - - -# Function to print the table of animals -def print_animal_table(animals): - # Print table header - print("\nšŸ¾ " + Fore.CYAN + "List of Animals" + Style.RESET_ALL + " šŸ¾") - print("+-------------------------------------------------------------------------------------------+") - print("| " + Fore.YELLOW + "Name".ljust(20) + Style.RESET_ALL + "| " + Fore.YELLOW + "Species".ljust(8) + Style.RESET_ALL + "| " + Fore.YELLOW + "Breed".ljust(25) + Style.RESET_ALL + "| " + Fore.YELLOW + "Gender".ljust(15) + Style.RESET_ALL + "| " + Fore.YELLOW + "Age".ljust(1) + Style.RESET_ALL + " | " + Fore.YELLOW + "Adopted".ljust(7) + Style.RESET_ALL + " |") - print("+-------------------------------------------------------------------------------------------+") - - # Print each animal's data row by row - for animal in animals: - name_column = f"| {animal['name'].ljust(20)}" - species_column = f"| {animal['species'].ljust(8)}" - breed_column = f"| {animal['breed'].ljust(25)}" - gender_column = f"| {animal['gender'].ljust(15)}" - age_column = f"| {str(animal['age']).ljust(3)}" - adopted_column = f"| {str(animal['adopted']).ljust(7)} |" - print(name_column + species_column + breed_column + gender_column + age_column + adopted_column) - - # Print table footer - print("+-------------------------------------------------------------------------------------------+") +select_option = "\nPlease select an option: " +invalid_input = "\nInvalid input! Please enter a valid index." # Function to filter animals based on user input def filter_animals(animals): @@ -80,69 +61,80 @@ def filter_animals(animals): clear_screen() print_animal_table(animals) -# Function to search animals based on user input def search_animals(animals, current_user): - while True: - clear_screen() - print(Fore.LIGHTCYAN_EX + "\nšŸ”Ž SEARCH ANIMALS šŸ”" + Style.RESET_ALL) - print("\nEnter any of the following criteria to search:") - print(Fore.GREEN + "\n - Name") - print(" - Species") - print(" - Breed") - print(" - Gender (Male/Female)") - print(" - Age" + Style.RESET_ALL) - print("\nOr type 'exit' to return to the main menu.") - - search_query = input(Fore.LIGHTCYAN_EX + "\nSearch: " + Style.RESET_ALL).lower() + print_search_prompt() + search_query = get_search_query() if search_query == 'exit': - clear_screen() - print_animal_table(animals) + clear_screen_and_print_animals(animals) return log_action(current_user, f"Searched for {search_query}") - found_results = [] - - if search_query.strip(): - for animal in animals: - if (search_query in animal['name'].lower() or - search_query in animal['species'].lower() or - search_query in animal['breed'].lower() or - (search_query == 'male' and animal['gender'].lower() == 'male') or - (search_query == 'female' and animal['gender'].lower() == 'female') or - search_query == str(animal['age'])): - found_results.append(animal) - - if found_results: - clear_screen() - print(Fore.LIGHTYELLOW_EX + "SEARCH RESULTS" + Style.RESET_ALL) - print_animal_table(found_results) - print("\n1. " + Fore.GREEN + "Search for another animal" + Style.RESET_ALL) - print("2. " + Fore.YELLOW + "Exit" + Style.RESET_ALL) - exit_input = input("\nPlease select an option: ") - - if exit_input == '1': - continue - elif exit_input == '2': - clear_screen() - print_animal_table(animals) - return - else: - print(Fore.RED + "Invalid input. Please choose one of the options." + Style.RESET_ALL) - time.sleep(2) - clear_screen() - print_animal_table(animals) - else: - print(Fore.RED + "No animals found matching the search criteria" + Style.RESET_ALL) - time.sleep(2) - clear_screen() - print_animal_table(animals) + found_results = search_animals_by_query(animals, search_query) + + if found_results: + handle_found_results(animals, found_results) else: - print(Fore.RED + "Invalid input. Please enter a search query or type 'exit'." + Style.RESET_ALL) - time.sleep(2) - clear_screen() + handle_no_results(animals) + +def print_search_prompt(): + clear_screen() + print(Fore.LIGHTCYAN_EX + "\nšŸ”Ž SEARCH ANIMALS šŸ”" + Style.RESET_ALL) + print("\nEnter any of the following criteria to search:") + print(Fore.GREEN + "\n - Name") + print(" - Species") + print(" - Breed") + print(" - Gender (Male/Female)") + print(" - Age" + Style.RESET_ALL) + print("\nOr type 'exit' to return to the main menu.") + +def get_search_query(): + return input(Fore.LIGHTCYAN_EX + "\nSearch: " + Style.RESET_ALL).lower() + +def search_animals_by_query(animals, search_query): + found_results = [] + if search_query.strip(): + for animal in animals: + if (search_query in animal['name'].lower() or + search_query in animal['species'].lower() or + search_query in animal['breed'].lower() or + (search_query == 'male' and animal['gender'].lower() == 'male') or + (search_query == 'female' and animal['gender'].lower() == 'female') or + search_query == str(animal['age'])): + found_results.append(animal) + return found_results + +def handle_found_results(animals, found_results): + clear_screen() + print(Fore.LIGHTYELLOW_EX + "SEARCH RESULTS" + Style.RESET_ALL) + print_animal_table(found_results) + print("\n1. " + Fore.GREEN + "Search for another animal" + Style.RESET_ALL) + print("2. " + Fore.YELLOW + "Exit" + Style.RESET_ALL) + exit_input = input(select_option) + + if exit_input == '1': + return + elif exit_input == '2': + clear_screen_and_print_animals(animals) + return + else: + print_invalid_input(animals) + +def handle_no_results(animals): + print(Fore.RED + "No animals found matching the search criteria" + Style.RESET_ALL) + time.sleep(2) + clear_screen_and_print_animals(animals) + +def print_invalid_input(animals): + print(Fore.RED + invalid_input + Style.RESET_ALL) + time.sleep(2) + clear_screen_and_print_animals(animals) + +def clear_screen_and_print_animals(animals): + clear_screen() + print_animal_table(animals) # Function to sort animals based on user input def sort_animals(animals, key='name', reverse=False): @@ -158,75 +150,87 @@ def sort_animals(animals, key='name', reverse=False): return sorted_animals def delete_animal(animal_name): - try: - # Check if there are multiple animals with the same name animal_count = animals_collection.count_documents({"name": animal_name}) - + if animal_count == 0: - print(f"{animal_name} not found in the database.") - time.sleep(2) - clear_screen() - print_animal_table(load_animal_data(animals_collection)) - return False - + handle_no_animal_found(animal_name) elif animal_count == 1: - result = animals_collection.delete_one({"name": animal_name}) - if result.deleted_count == 1: - print(Fore.GREEN + f"\nSuccessfully deleted {animal_name} from the database." + Style.RESET_ALL) - time.sleep(2) - clear_screen() - print_animal_table(load_animal_data(animals_collection)) - return True - else: - print(f"Failed to delete {animal_name} from the database.") - time.sleep(2) - clear_screen() - print_animal_table(load_animal_data(animals_collection)) - return False - - else: # Multiple animals with the same name - print(Fore.YELLOW + f"Multiple animals found with the name '{animal_name}'. Please select the index of the animal you want to delete:" + Style.RESET_ALL) - - # Find all animals with the same name and print their indices - cursor = animals_collection.find({"name": animal_name}) - index = 1 - for animal in cursor: - print(f"{index}. Name: {animal['name']}, Species: {animal['species']}, Breed: {animal['breed']}") - index += 1 - - # Prompt the user to select the index of the animal to delete - selected_index = input("\nEnter the index of the animal to delete: ") - try: - selected_index = int(selected_index) - if 1 <= selected_index <= animal_count: - cursor.rewind() # Reset cursor to the beginning - selected_animal = cursor[selected_index - 1] - result = animals_collection.delete_one({"_id": selected_animal["_id"]}) - if result.deleted_count == 1: - print(Fore.GREEN + f"\nSuccessfully deleted {selected_animal['name']} from the database." + Style.RESET_ALL) - time.sleep(2) - clear_screen() - return True - else: - print(f"Failed to delete {selected_animal['name']} from the database.") - time.sleep(2) - clear_screen() - return False - else: - print(Fore.RED + "Invalid index. Please enter a valid index." + Style.RESET_ALL) - time.sleep(2) - clear_screen() - return False - except ValueError: - print(Fore.RED + "Invalid index. Please enter a valid index." + Style.RESET_ALL) - time.sleep(2) - clear_screen() - return False + delete_single_animal(animal_name) + else: + delete_multiple_animals(animal_name, animal_count) except Exception as e: print(f"An error occurred: {e}") - return False + return False + +def handle_no_animal_found(animal_name): + print(f"{animal_name} not found in the database.") + time.sleep(2) + clear_screen_and_print_animals() + return False + +def delete_single_animal(animal_name): + result = animals_collection.delete_one({"name": animal_name}) + if result.deleted_count == 1: + print(Fore.GREEN + f"\nSuccessfully deleted {animal_name} from the database." + Style.RESET_ALL) + time.sleep(2) + clear_screen_and_print_animals() + return True + else: + print(f"Failed to delete {animal_name} from the database.") + time.sleep(2) + clear_screen_and_print_animals() + return False + +def delete_multiple_animals(animal_name, animal_count): + print(Fore.YELLOW + f"Multiple animals found with the name '{animal_name}'. Please select the index of the animal you want to delete:" + Style.RESET_ALL) + cursor = animals_collection.find({"name": animal_name}) + print_animals_with_indices(cursor) + selected_index = get_selected_index() + + if 1 <= selected_index <= animal_count: + delete_selected_animal(cursor, selected_index) + else: + print_invalid_index() + +def print_animals_with_indices(cursor): + index = 1 + for animal in cursor: + print(f"{index}. Name: {animal['name']}, Species: {animal['species']}, Breed: {animal['breed']}") + index += 1 + +def get_selected_index(): + selected_index = input("\nEnter the index of the animal to delete: ") + try: + return int(selected_index) + except ValueError: + print_invalid_index() + +def delete_selected_animal(cursor, selected_index): + cursor.rewind() # Reset cursor to the beginning + selected_animal = cursor[selected_index - 1] + result = animals_collection.delete_one({"_id": selected_animal["_id"]}) + if result.deleted_count == 1: + print(Fore.GREEN + f"\nSuccessfully deleted {selected_animal['name']} from the database." + Style.RESET_ALL) + time.sleep(2) + clear_screen() + return True + else: + print(f"Failed to delete {selected_animal['name']} from the database.") + time.sleep(2) + clear_screen() + return False + +def print_invalid_index(): + print(Fore.RED + "Invalid index. Please enter a valid index." + Style.RESET_ALL) + time.sleep(2) + clear_screen() + return False + +def clear_screen_and_print_animals(): + clear_screen() + print_animal_table(load_animal_data(animals_collection)) def modify_animal_database(): @@ -238,7 +242,7 @@ def modify_animal_database(): print("3. " + Fore.GREEN + "Delete Animal" + Style.RESET_ALL) print("4. " + Fore.YELLOW + "Exit" + Style.RESET_ALL) - choice = input("\nPlease select an option: ") + choice = input(select_option) # Add Animal if choice == '1': @@ -273,7 +277,7 @@ def modify_animal_database(): return else: - print(Fore.RED + "\nInvalid input. Please choose one of the options." + Style.RESET_ALL) + print(Fore.RED + invalid_input + Style.RESET_ALL) time.sleep(2) clear_screen() print_animal_table(animals) @@ -282,104 +286,106 @@ def modify_animal_database(): # Function to view animals and interact with options def view_animals(): - animals = load_animal_data(animals_collection) - current_user = sudo_user() # Load animal data print_animal_table(animals) while True: - print(Fore.CYAN + "\nāš™ļø Options āš™ļø" + Style.RESET_ALL) - print("\n1. " + Fore.GREEN + "Search for animal" + Style.RESET_ALL) - print("2. " + Fore.GREEN + "Sort/Filter Animals" + Style.RESET_ALL) - - - if current_user['level'] >= 2: - print("3. " + Fore.GREEN + "View animal profile" + Style.RESET_ALL) - print("4. " + Fore.GREEN + "Modify Database" + Style.RESET_ALL) - print("5. " + Fore.YELLOW + "Exit" + Style.RESET_ALL) - else: - print("3. " + Fore.YELLOW + "Exit" + Style.RESET_ALL) + print_options(current_user) + user_input = input(select_option) - user_input = input("\nPlease select an option: ") - - # Search Database if user_input == '1': - time.sleep(1) - clear_screen() - search_animals(animals, current_user) - - # Sort Database + search_database(animals, current_user) elif user_input == '2': - sort_filter_option = input("\nSort or filter? (S/F): ").lower() - if sort_filter_option == 's': - clear_screen() - print_animal_table(animals) - print("\nSort by:") - print("1." + Fore.GREEN + " Name (A-Z)" + Style.RESET_ALL) - print("2." + Fore.GREEN + " Name (Z-A)" + Style.RESET_ALL) - print("3." + Fore.GREEN + " Age (Youngest to Oldest)" + Style.RESET_ALL) - print("4." + Fore.GREEN + " Age (Oldest to Youngest)" + Style.RESET_ALL) - sort_option = input("\nPlease select an option: ") - - # Sort animals based on user input - if sort_option in ['1', '2', '3', '4']: - sort_key = 'name' if sort_option in ['1', '2'] else 'age' - reverse_sort = True if sort_option in ['2', '4'] else False - animals = sort_animals(animals, key=sort_key, reverse=reverse_sort) - clear_screen() - print_animal_table(animals) - - # Invalid option - else: - print(Fore.RED + "\nInvalid input. Please choose one of the options." + Style.RESET_ALL) - time.sleep(2) - clear_screen() - print_animal_table(animals) - - # Filter Database - elif sort_filter_option == 'f': - filter_animals(animals) - - # Invalid option - else: - print(Fore.RED + "\nInvalid input. Please choose one of the options." + Style.RESET_ALL) - time.sleep(2) - clear_screen() - print_animal_table(animals) - - # View Animal Profile + sort_or_filter_database(animals) elif user_input == '3': - if current_user['level'] >= 2: - view_animals_full() - else: - log_action(current_user, "Exited 'View Animal Database'") - print("\nExiting...") - time.sleep(2) - clear_screen() - return - - elif user_input == '4': - if current_user['level'] >= 2: - log_action(current_user, "Entered 'Modify Animal Database'") - time.sleep(2) - clear_screen() - modify_animal_database() - - # Exit Database + view_animal_profile_or_exit(current_user) + elif user_input == '4' and current_user['level'] >= 2: + modify_database(current_user) elif user_input == '5' and current_user['level'] >= 2: - log_action(current_user, "Exited 'View Animal Database'") - print("\nExiting...") - time.sleep(2) - clear_screen() - return + exit_database(current_user) else: - print(Fore.RED + "\nInvalid input. Please choose one of the options." + Style.RESET_ALL) - time.sleep(2) - clear_screen() - print_animal_table(animals) + handle_invalid_input(animals) + +# Print options for the user +def print_options(current_user): + print(Fore.CYAN + "\nāš™ļø Options āš™ļø" + Style.RESET_ALL) + print("\n1. " + Fore.GREEN + "Search for animal" + Style.RESET_ALL) + print("2. " + Fore.GREEN + "Sort/Filter Animals" + Style.RESET_ALL) + + if current_user['level'] >= 2: + print("3. " + Fore.GREEN + "View animal profile" + Style.RESET_ALL) + print("4. " + Fore.GREEN + "Modify Database" + Style.RESET_ALL) + print("5. " + Fore.YELLOW + "Exit" + Style.RESET_ALL) + else: + print("3. " + Fore.YELLOW + "Exit" + Style.RESET_ALL) + +# Search the database +def search_database(animals, current_user): + time.sleep(1) + clear_screen() + search_animals(animals, current_user) + +# Sort or filter the database +def sort_or_filter_database(animals): + sort_filter_option = input("\nSort or filter? (S/F): ").lower() + if sort_filter_option == 's': + sort_database(animals) + elif sort_filter_option == 'f': + filter_animals(animals) + else: + handle_invalid_input(animals) + +# Sort the database +def sort_database(animals): + clear_screen() + print_animal_table(animals) + print("\nSort by:") + print("1." + Fore.GREEN + " Name (A-Z)" + Style.RESET_ALL) + print("2." + Fore.GREEN + " Name (Z-A)" + Style.RESET_ALL) + print("3." + Fore.GREEN + " Age (Youngest to Oldest)" + Style.RESET_ALL) + print("4." + Fore.GREEN + " Age (Oldest to Youngest)" + Style.RESET_ALL) + sort_option = input(select_option) + + # Sort animals based on user input + if sort_option in ['1', '2', '3', '4']: + sort_key = 'name' if sort_option in ['1', '2'] else 'age' + reverse_sort = True if sort_option in ['2', '4'] else False + animals = sort_animals(animals, key=sort_key, reverse=reverse_sort) + clear_screen() + print_animal_table(animals) + else: + handle_invalid_input(animals) + +# View animal profile or exit +def view_animal_profile_or_exit(current_user): + if current_user['level'] >= 2: + view_animals_full() + else: + exit_database(current_user) + +# Modify the database +def modify_database(current_user): + log_action(current_user, "Entered 'Modify Animal Database'") + time.sleep(2) + clear_screen() + modify_animal_database() + +# Exit the database +def exit_database(current_user): + log_action(current_user, "Exited 'View Animal Database'") + print("\nExiting...") + time.sleep(2) + clear_screen() + +# Handle invalid input +def handle_invalid_input(animals): + print(Fore.RED + invalid_input + Style.RESET_ALL) + time.sleep(2) + clear_screen() + print_animal_table(animals) if __name__ == "__main__": view_animals() \ No newline at end of file