Cleaning Service Booking System in Python Tkinter

Cleaning Service Booking System in Python Tkinter

Introduction

Hello friends, in this article we will see a very simple cleaning service booking system in Python tkinter. This project can not be used in the real world, this just tries to mimic real-world online service applications and that’s why the features of this app are limited. In simple words, you can use this app to book a cleaner. I have used the following technologies for the project- Python, tkinter, tkcalendar, re, and sqlite3.

Working of Cleaning Service Booking System

Users

There are two types of users- Cleaner and Customer. Initially, when you run the app, the global Home screen will open and you will see 3 options- Login, Register, and Close App.

home screen

We can click on the Register button if we are not registered, after clicking, we will see the Register screen.

registration screen

You can register as a cleaner or customer. After registration, we will be directed to the Home screen where we can click the Login button.

After we click the Login button, we will see the Login screen.

login screen

Customer side working

If you log in as a customer, you will see 5 options-

customer home screen

If you click on book a cleaner, you will see options to book a cleaner

booking screen

You can also see your booking history

customer history screen

You can click on About Us to learn about the cleaning service provider company, Logout to logout from the App, and Close App button to exit the app.

Cleaner Side working

You will see 4 options after you logged in as cleaner-

cleaner home screen

If you click on Check Current Bookings, you will see the date and time, customer name, and customer phone who has booked you for the cleaning service through the app.

cleaner check current screen

The other 3 options(About Us, Logout, and Close APP) are the same as they are for customers.

Complete Source code for Cleaning Service Booking System in Python Tkinter

import tkinter as tk
import sqlite3
from tkinter import messagebox
from tkcalendar import DateEntry
from tkinter import ttk
import re

def is_valid_phone_number(phone_number):
    # Define the regex pattern for a valid phone number
    # This regex pattern allows for 10 digits with optional dashes or spaces
    # Examples of valid phone numbers: "1234567890", "123-456-7890", "123 456 7890"
    pattern = r'^\d{3}[-\s]?\d{3}[-\s]?\d{4}$'

    # Check if the phone number matches the regex pattern
    return re.match(pattern, phone_number)

def is_strong_password(password):

    if len(password) < 8:
        return False

    if not any(char.isupper() for char in password):
        return False

    if not any(char.islower() for char in password):
        return False

    if not any(char.isdigit() for char in password):
        return False

    if not re.search(r'[!@#$%^&*]', password):
        return False

    return True

# SQLite Database Initialization
# Create a database and tables to store user and booking information
conn = sqlite3.connect("book_cleaning_service.db")
cursor = conn.cursor()

# Create a User table to store user information
cursor.execute("""
CREATE TABLE IF NOT EXISTS User (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    phone TEXT NOT NULL,
    password TEXT NOT NULL,
    user_type TEXT NOT NULL
)
""")

# Create a Booking table to store booking information
cursor.execute("""
CREATE TABLE IF NOT EXISTS Booking (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    customer_id INTEGER NOT NULL,
    cleaner_id INTEGER NOT NULL,
    date_time TEXT NOT NULL,
    FOREIGN KEY (customer_id) REFERENCES User (id),
    FOREIGN KEY (cleaner_id) REFERENCES User (id)
)
""")

conn.commit()

# Tkinter App Initialization
root = tk.Tk()
root.title("Cleaning Service Booking App")

# Function to handle user registration
def register_user():
    clear_screen()
    # Add code to handle user registration and insert user data into the database
    # Function to handle the user registration form submission
    def submit_registration():
        name = entry_name.get()
        phone = entry_phone.get()
        password = entry_password.get()
        user_type = user_type_var.get()
        if len(name)<5:
            messagebox.showerror("Name field Error", "Name should be 5 or more characters long.")
            return
        if not is_valid_phone_number(phone):
            messagebox.showerror("Phone field Error", "Please enter valid phone number")
            return
        if not is_strong_password(password):
            messagebox.showerror('Password field error', '''
    Criteria for a strong password
    At least 8 characters long
    Contains at least one uppercase letter
    Contains at least one lowercase letter
    Contains at least one digit
    Contains at least one special character (e.g., !, @, #, $, %, ^, &, *)''')
            return
            
        # Validate the input (you may add more validation checks as needed)
        if not name or not phone or not password:
            messagebox.showerror("Error", "Please fill in all fields.")
            return

        # Insert user data into the database
        try:
            conn = sqlite3.connect("book_cleaning_service.db")
            cursor = conn.cursor()

            # Check if a user with the same phone number already exists
            cursor.execute("SELECT * FROM User WHERE phone=?", (phone,))
            existing_user = cursor.fetchone()

            if existing_user:
                messagebox.showerror("Error", "User with this phone number already exists.")
                conn.close()
                return

            # Insert the new user into the User table
            cursor.execute("INSERT INTO User (name, phone, password, user_type) VALUES (?, ?, ?, ?)",
                           (name, phone, password, user_type))  # Assuming the user is registering as a customer
            conn.commit()

            messagebox.showinfo("Success", "Registration successful.")
            # root.destroy()
            login_user()

        except sqlite3.Error as e:
            messagebox.showerror("Error", f"Error while registering: {str(e)}")

        finally:
            conn.close()

    # Create labels and entry fields for the registration form
    label_name = tk.Label(root, text="Name:")
    entry_name = tk.Entry(root)

    label_phone = tk.Label(root, text="Phone:")
    entry_phone = tk.Entry(root)

    label_password = tk.Label(root, text="Password:")
    entry_password = tk.Entry(root, show="*")
    
    user_type_label = tk.Label(root, text="User Type")
    user_type_var = tk.StringVar(root)
    user_type_var.set("Customer")  # Default value
    user_type_options = ["Customer", "Cleaner"]
    user_type_dropdown = tk.OptionMenu(root, user_type_var, *user_type_options)

    btn_register = tk.Button(root, text="Register", command=submit_registration)

    # Grid layout for the widgets
    label_name.grid(row=0, column=0, padx=10, pady=5)
    entry_name.grid(row=0, column=1, padx=10, pady=5)

    label_phone.grid(row=1, column=0, padx=10, pady=5)
    entry_phone.grid(row=1, column=1, padx=10, pady=5)

    label_password.grid(row=2, column=0, padx=10, pady=5)
    entry_password.grid(row=2, column=1, padx=10, pady=5)
    
    user_type_label.grid(row=3, column=0, padx=10, pady=5)
    user_type_dropdown.grid(row=3, column=1, padx=10, pady=5)

    btn_register.grid(row=4, column=0, columnspan=2, padx=10, pady=10)

# Function to handle user login
def login_user():
    clear_screen()
    # Function to handle the user login form submission
    def submit_login():
        global phone
        phone = entry_phone.get()
        password = entry_password.get()

        global user_type
        user_type = user_type_var.get()
        # Validate the input (you may add more validation checks as needed)
        if not phone or not password:
            messagebox.showerror("Error", "Please fill in all fields.")
            return

        # Check the login credentials in the database
        try:
            conn = sqlite3.connect("book_cleaning_service.db")
            cursor = conn.cursor()

            # Retrieve user data based on the phone number
            cursor.execute("SELECT * FROM User WHERE phone=?", (phone,))
            user = cursor.fetchone()

            if user is None:
                messagebox.showerror("Error", "User not found.")
                conn.close()
                return

            # Check if the entered password matches the stored password
            if user[3] == password:
                # Redirect the user based on the user_type (customer or cleaner)
                if user_type == user[4]:
                    if user_type == 'Customer':
                        # Redirect to the customer screen
                        show_customer_screen()
                    elif user_type == "Cleaner":
                        # Redirect to the cleaner screen
                        show_cleaner_screen()
                    else:
                        messagebox.showerror("Error", "Invalid user type.")
                else:
                    messagebox.showerror('Error', 'User type mismatch')
            else:
                messagebox.showerror("Error", "Incorrect password.")

        except sqlite3.Error as e:
            messagebox.showerror("Error", f"Error while logging in: {str(e)}")

        finally:
            conn.close()

    # Create labels and entry fields for the login form
    label_phone = tk.Label(root, text="Phone:")
    entry_phone = tk.Entry(root)

    label_password = tk.Label(root, text="Password:")
    entry_password = tk.Entry(root, show="*")

    user_type_label = tk.Label(root, text="User Type")
    user_type_var = tk.StringVar(root)
    user_type_var.set("Customer")  # Default value
    user_type_options = ["Customer", "Cleaner"]
    user_type_dropdown = tk.OptionMenu(root, user_type_var, *user_type_options)

    btn_login = tk.Button(root, text="Login", command=submit_login)

    # Grid layout for the widgets
    label_phone.grid(row=0, column=0, padx=10, pady=5)
    entry_phone.grid(row=0, column=1, padx=10, pady=5)

    label_password.grid(row=1, column=0, padx=10, pady=5)
    entry_password.grid(row=1, column=1, padx=10, pady=5)
    
    user_type_label.grid(row=2, column=0, padx=10, pady=5)
    user_type_dropdown.grid(row=2, column=1, padx=10, pady=5)

    btn_login.grid(row=3, column=0, columnspan=2, padx=10, pady=10)

# Function to handle booking a cleaner
def book_cleaner():
    clear_screen()
    # Function to handle the selection of a cleaner
    def on_select_cleaner(event):
        # Clear any previous booking date and time selection
        date_entry.delete(0, tk.END)

        # Get the selected item (cleaner) from the treeview
        selected_item = cleaners_tree.selection()
        if selected_item:
            cleaner_name = cleaners_tree.item(selected_item, "values")[0]
            cleaner_phone = cleaners_tree.item(selected_item, "values")[1]
            selected_cleaner_label.config(text=f"Selected Cleaner: {cleaner_name} - {cleaner_phone}")

    # Function to handle the booking form submission
    def submit_booking():
        # Get the selected cleaner's name and phone
        selected_item = cleaners_tree.selection()
        if selected_item:
            cleaner_name = cleaners_tree.item(selected_item, "values")[0]
            cleaner_phone = cleaners_tree.item(selected_item, "values")[1]

            # Get the selected date and time for booking
            selected_date_time = date_entry.get()

            # You can now process the booking details and save to the database
            boolean_value = save_booking(cleaner_phone, selected_date_time)

            if boolean_value==True:
                tk.messagebox.showinfo("Booking Successful", f"Booking for {cleaner_name} - {cleaner_phone} on {selected_date_time} is successful!")
            if user_type=='Customer':
                show_customer_screen()
            else:
                show_cleaner_screen()

    # Function to save booking details to the database
    def save_booking(cleaner_phone, booking_date_time):
        try:
            conn = sqlite3.connect("book_cleaning_service.db")
            cursor = conn.cursor()

            # Fetch the cleaner ID from the database
            cursor.execute("SELECT id FROM User WHERE phone=?", (cleaner_phone,))
            cleaner_id = cursor.fetchone()[0]
            
            cursor.execute('SELECT id FROM User WHERE phone=?', (phone,))
            customer_id = cursor.fetchone()[0]

            # Insert the booking details into the database
            cursor.execute("INSERT INTO Booking (customer_id, cleaner_id, date_time) VALUES (?, ?, ?)", (customer_id, cleaner_id, booking_date_time))
            conn.commit()
            return True

        except sqlite3.Error as e:
            tk.messagebox.showerror("Error", f"Error while saving booking: {str(e)}")
            print("Error", f"Error while saving booking: {str(e)}")

        finally:
            conn.close()
            
    root.title("Book a Cleaner")

    # Fetch the list of cleaners from the database
    cleaners_list = []
    try:
        conn = sqlite3.connect("book_cleaning_service.db")
        cursor = conn.cursor()

        cursor.execute("SELECT name, phone FROM User WHERE user_type=?", ('Cleaner',))
        cleaners_list = cursor.fetchall()

    except sqlite3.Error as e:
        tk.messagebox.showerror("Error", f"Error while fetching cleaners: {str(e)}")

    finally:
        conn.close()

    # Create a treeview to display the cleaners' details
    cleaners_tree = ttk.Treeview(root, columns=("Name", "Phone"), show="headings")
    cleaners_tree.heading("Name", text="Name")
    cleaners_tree.heading("Phone", text="Phone")

    # Add the cleaners' details to the treeview
    for cleaner in cleaners_list:
        cleaners_tree.insert("", tk.END, values=cleaner)

    # Bind the selection event of the treeview to the on_select_cleaner function
    cleaners_tree.bind("<<TreeviewSelect>>", on_select_cleaner)

    # Create a label to show the selected cleaner's details
    selected_cleaner_label = tk.Label(root, text="Selected Cleaner: None")

    # Create a date picker for booking date
    date_entry = DateEntry(root, width=12, background="darkblue", foreground="white", borderwidth=2)

    # Create a button to submit the booking
    btn_book = tk.Button(root, text="Book", command=submit_booking)

    # Grid layout for the widgets
    cleaners_tree.grid(row=0, column=0, columnspan=2, padx=10, pady=5)
    selected_cleaner_label.grid(row=1, column=0, columnspan=2, padx=10, pady=5)
    date_entry.grid(row=2, column=0, padx=10, pady=5)
    btn_book.grid(row=2, column=1, padx=10, pady=5)
    
# Function to handle viewing booking history
# Function to handle viewing booking history
def view_booking_history():
    clear_screen()
    # Create the booking history window
    root.title("Your Booking History")

    # Get the customer's phone number (you can set this variable when the customer logs in)
    customer_phone = phone

    # Retrieve the customer's user ID based on their phone number
    try:
        conn = sqlite3.connect("book_cleaning_service.db")
        cursor = conn.cursor()

        cursor.execute("SELECT id FROM User WHERE phone=?", (customer_phone,))
        customer_id = cursor.fetchone()[0]

        # Fetch the booking history for the customer from the Booking table
        # cursor.execute("SELECT * FROM Booking WHERE customer_id=?", (customer_id,))
        cursor.execute("SELECT b.date_time, u.name, u.phone FROM Booking b JOIN User u ON b.cleaner_id = u.id WHERE b.customer_id=?", (customer_id,))
        booking_history = cursor.fetchall()

    except sqlite3.Error as e:
        messagebox.showerror("Error", f"Error while fetching booking history: {str(e)}")
        booking_history = []

    finally:
        conn.close()

    # Check if any bookings were found in the database
    if not booking_history:
        messagebox.showinfo("No Bookings", "No booking history available.")
        return

    # Create a listbox to display the booking history
    root.geometry('500x300')
    listbox_booking_history = tk.Listbox(root, width=150)
    for i in booking_history:
        print(i)
    # Add the booking history data to the listbox
    for booking in booking_history:
        date_time, cleaner_name, cleaner_phone = booking
        listbox_booking_history.insert(tk.END, f"Date and Time: {date_time} - Cleaner Name: {cleaner_name} - Cleaner Phone: {cleaner_phone}")

    # Grid layout for the listbox
    listbox_booking_history.pack(padx=10, pady=10)
    if user_type=='Customer':
        btn_to_home = tk.Button(root, text="Go to Home", command=show_customer_screen)
    else:
        btn_to_home = tk.Button(root, text='Go to Home', command=show_cleaner_screen)

    # Grid layout for the label
    btn_to_home.pack(padx=10, pady=10)

# Function to handle checking current bookings
def check_current_bookings():
    clear_screen()
    root.geometry('500x300')
    root.title("Current Bookings")

    # Retrieve the cleaner's user ID based on their phone number
    try:
        conn = sqlite3.connect("book_cleaning_service.db")
        cursor = conn.cursor()

        cursor.execute("SELECT id FROM User WHERE phone=?", (phone,))
        cleaner_id = cursor.fetchone()[0]

        # Fetch the current bookings for the cleaner from the Booking table
        cursor.execute("SELECT b.date_time, u.name, u.phone FROM Booking b JOIN User u ON b.customer_id = u.id WHERE b.cleaner_id=?", (cleaner_id,))
        current_bookings = cursor.fetchall()

    except sqlite3.Error as e:
        messagebox.showerror("Error", f"Error while fetching current bookings: {str(e)}")
        current_bookings = []

    finally:
        conn.close()

    # Check if any current bookings were found in the database
    if not current_bookings:
        messagebox.showinfo("No Current Bookings", "No current bookings found.\nReturning to Home page")
        if user_type=='Customer':
            show_customer_screen()
        else:
            show_cleaner_screen()
        return

    # Create a listbox to display the current bookings
    listbox_current_bookings = tk.Listbox(root, width=150)

    # Add the current bookings data to the listbox
    for booking in current_bookings:
        date_time, customer_name, customer_phone = booking
        listbox_current_bookings.insert(tk.END, f"Date and Time: {date_time} - Customer Name: {customer_name} - Customer Phone: {customer_phone}")

    if user_type=='Customer':
        btn_to_home = tk.Button(root, text="Go to Home", command=show_customer_screen)
    else:
        btn_to_home = tk.Button(root, text='Go to Home', command=show_cleaner_screen)
    # Grid layout for the listbox
    listbox_current_bookings.pack(padx=10, pady=10)
    btn_to_home.pack(padx=10, pady=10)

# Function to show the "About Us" screen
# Function to show the "About Us" screen
def show_about_us():
    clear_screen()
    root.geometry("600x300")

    # Define the "About Us" information (you can replace this with your actual information)
    about_us_info = """Welcome to our Cleaning Service Company!
    
                       Our mission is to provide top-quality cleaning services to our customers at affordable prices. With a team of skilled and reliable cleaners, we are dedicated to making your home or office a clean and pleasant environment.
                       
                       Contact us today to book a cleaner and experience the difference our cleaning service can make in your life."""

    # Create a label to display the "About Us" information
    label_about_us = tk.Label(root, text=about_us_info, wraplength=400, justify=tk.LEFT)
    
    if user_type=='Customer':
        btn_to_home = tk.Button(root, text="Go to Home", command=show_customer_screen)
    else:
        btn_to_home = tk.Button(root, text='Go to Home', command=show_cleaner_screen)

    # Grid layout for the label
    label_about_us.pack(padx=10, pady=10)
    btn_to_home.pack(padx=10, pady=10)

# Function to show the home screen
def show_home_screen():
    clear_screen()
    root.title('Cleaning Service booking App')

    # Create labels and buttons for login and registration
    label_title = tk.Label(root, text="Welcome to Cleaning Service")
    btn_login = tk.Button(root, text="Login", command=login_user)
    btn_register = tk.Button(root, text="Register", command=register_user)
    btn_close = tk.Button(root, text="Close App", command=close_app)

    # Grid layout for the widgets
    label_title.pack(pady=20)
    btn_login.pack(pady=10)
    btn_register.pack(pady=10)
    btn_close.pack(pady=10)
    
# Function to show the customer screen
def show_customer_screen():
    clear_screen()

    # Create buttons for customer options
    btn_book_cleaner = tk.Button(root, text="Book a Cleaner", command=book_cleaner)
    btn_view_booking_history = tk.Button(root, text="View Booking History", command=view_booking_history)
    btn_about_us = tk.Button(root, text="About Us", command=show_about_us)
    btn_logout = tk.Button(root, text="Logout", command=show_home_screen)
    btn_close = tk.Button(root, text="Close App", command=close_app)

    # Grid layout for the buttons
    btn_book_cleaner.pack(pady=10)
    btn_view_booking_history.pack(pady=10)
    btn_about_us.pack(pady=10)
    btn_logout.pack(pady=10)
    btn_close.pack(pady=10)
    
# Function to show the cleaner screen
def show_cleaner_screen():
    clear_screen()

    # Create buttons for cleaner options
    btn_check_current_bookings = tk.Button(root, text="Check Current Bookings", command=check_current_bookings)
    btn_about_us = tk.Button(root, text="About Us", command=show_about_us)
    btn_logout = tk.Button(root, text="Logout", command=show_home_screen)
    btn_close = tk.Button(root, text="Close App", command=close_app)

    # Grid layout for the buttons
    btn_check_current_bookings.pack(pady=10)
    btn_about_us.pack(pady=10)
    btn_logout.pack(pady=10)
    btn_close.pack(pady=10)

# Function to clear the screen
def clear_screen():
    for widget in root.winfo_children():
        widget.destroy()
    
def close_app():
    root.destroy()    
# Main program
if __name__ == "__main__":
    root = tk.Tk()
    root.title("Cleaning Service booking App")
    root.geometry("400x300")
    show_home_screen()
    root.mainloop()

Check out my GitHub repository for the source code.

Share:

Author: Yogesh Kumar