Alex Narb

Python Developer • Future Quant • Systems Thinker

First Project

Shopping Cart Program


# Shopping Cart Program => A simple application that allows users to add items to a virtual shopping cart,
# view the contents of the cart, and calculate the total cost of the items in the cart.
# It can be used as a basic e-commerce application or as a learning exercise for programming concepts such as classes, lists, and user input.
# In this program, we will create a ShoppingCart class that has methods for adding items, removing items, modifying items, viewing the cart, and checking out.
# The program will also include error handling for invalid input and edge cases, such as trying to remove an item that is not in the cart or trying to checkout with an empty cart.
# This program is a fun way to practice using classes, methods, lists, and user input in Python, and it can be expanded with additional features such as saving the cart to a file, applying discounts,
# or integrating with a payment system. The possibilities are endless, so feel free to get creative with your shopping cart program!

# Note: At the time of writing this, the program currently does not include a graphical user interface (GUI), but you can certainly add one if you want to practice using libraries such as Tkinter or PyQt.
# The current implementation is designed to be run in a terminal or command-line interface, and it provides a simple text-based menu for the user to interact with the shopping cart.
# But don't worry, the core functionality of the shopping cart is all there, and you can always enhance it with a GUI later on if you wish to do so.
# The important thing is to understand the underlying programming concepts and how to implement them in Python. Happy coding!
# But one thing is sure, I will make a GUI version in the future after I have completed the entire course, so stay tuned for that if you're interested in learning how to create graphical applications with Python!

# The program will also include a simple text-based menu for the user to interact with the shopping cart, allowing them to choose from options such as adding an item,
# removing an item, viewing the cart, modifying an item, checking out, or quitting the program.
# The program will continue to run until the user chooses to quit, and it will provide feedback on the actions taken, such as confirming when an item is added or
# removed, displaying the contents of the cart, and showing the total cost at checkout.
# The program will also include cutting-edge error handling to ensure that the user cannot enter invalid input, such as trying to add an item with a negative price or
# trying to remove an item that is not in the cart.

# Overall, this shopping cart program is a great way to practice fundamental programming concepts in Python while creating a useful and interactive application.
# By implementing this program, you will gain experience with classes, methods, lists, user input, error handling, and basic program flow control, all of which are essential skills for any Python programmer.
# By completing this project, you will have a functional shopping cart application that you can further enhance and customize as you continue to learn and grow as a Python developer. Happy coding!
# By far the most complex project in this course, so don't worry if it takes some time to understand and implement. Take it step by step, and feel free to ask for help if you get stuck. You've got this!

class ShoppingCart:
    def __init__(self):
        # Each entry is {"name": , "price": }
        self.items = []

    def _validate(self, item, price=None):
        if not item:
            raise ValueError("Item cannot be empty.")
        
        if not item.isalpha():
            raise ValueError("Item must contain only letters.")
        
        if price is not None and (not isinstance(price, (int, float)) or price < 0):
            raise ValueError("Price must be a non-negative number.")

    def add_item(self, item, price=None):
        self._validate(item, price)

        if not self.items:
            print("Your shopping cart is empty. Adding the first item.")

        stored_price = float(price) if price is not None else None

        self.items.append({"name": item, "price": stored_price})

        if stored_price is None:
            print(f"Added {item} to the shopping cart.")

        else:
            print(f"Added {item} (${stored_price:.2f}) to the shopping cart.")

    def add_price(self, item, price):
        self._validate(item, price)

        for entry in self.items:
            if entry["name"] == item:
                entry["price"] = float(price)
                print(f"Added price ${price:.2f} for {item}.")
                return
            
        print(f"{item} not found in the shopping cart. Cannot add price.")

    def remove_item(self, item):
        self._validate(item)

        if not self.items:
            print("Your shopping cart is empty. No items to remove.")
            return

        for i, entry in enumerate(self.items):
            if entry["name"] == item:
                removed = self.items.pop(i)

                if removed["price"] is None:
                    print(f"Removed {removed['name']} from the shopping cart.")

                else:
                    print(
                        f"Removed {removed['name']} (${removed['price']:.2f}) from the shopping cart."
                    )
                return
            
        print(f"{item} not found in the shopping cart.")

    def modify_item(self, item, new_item=None, new_price=None):
        self._validate(item)

        if new_item is not None:
            self._validate(new_item)

        if new_price is not None and (
            not isinstance(new_price, (int, float)) or new_price < 0
        ):
            
            raise ValueError("Price must be a non-negative number.")

        if not self.items:
            print("Your shopping cart is empty. No items to modify.")
            return

        for entry in self.items:
            if entry["name"] == item:
                if new_item is None and new_price is None:
                    print("No changes provided.")
                    return

                original_name = entry["name"]
                original_price = entry["price"]

                if new_item is not None:
                    entry["name"] = new_item

                if new_price is not None:
                    entry["price"] = float(new_price)

                old_price_text = (
                    "Not specified" if original_price is None else f"${original_price:.2f}"
                )
                new_price_text = (
                    "Not specified"
                    if entry["price"] is None
                    else f"${entry['price']:.2f}"
                )
                print(
                    f"Modified item: {original_name} ({old_price_text}) -> "
                    f"{entry['name']} ({new_price_text})"
                )
                return
            
        print(f"{item} not found in the shopping cart.")

    def __str__(self):
        if not self.items:
            return "Your shopping cart is empty."

        lines = ["Shopping Cart:"]

        total = 0.0

        for entry in self.items:
            if entry["price"] is None:
                lines.append(f"- {entry['name']} (Price: Not specified)")

            else:
                lines.append(f"- {entry['name']}: ${entry['price']:.2f}")
                total += entry["price"]

        lines.append(f"Total (priced items): ${total:.2f}")

        return "\n".join(lines)

    def view_cart(self):
        print(self)

    def checkout(self):
        if not self.items:
            print("Your shopping cart is empty. Nothing to checkout.")
            return

        total = sum(entry["price"] for entry in self.items if entry["price"] is not None)
        unpriced_items = [entry["name"] for entry in self.items if entry["price"] is None]

        print("\nCheckout Summary:")
        print(self)

        if unpriced_items:
            print(
                f"Note: {len(unpriced_items)} item(s) have no price and were not included in the total."
            )

        print(f"Amount due: ${total:.2f}")

        while True:
            confirm = prompt_input("Complete purchase? (yes/no): ", allow_blank=True)
            if confirm is None:
                return
            
            confirm = confirm.lower()
            
            if confirm in {"yes", "y"}:
                self.items.clear()

                print("Checkout complete. Thank you for your purchase!")
                return
            
            if confirm in {"no", "n", ""}:
                print("Checkout cancelled.")
                return
            
            print("Please enter yes, no, or 'esc'.")

def prompt_input(message, allow_blank=False):
    while True:
        raw = input(message).strip()

        if raw.lower() == "esc":
            print("Returning to main menu.")
            return None
        
        if allow_blank or raw:
            return raw
        print("Error: Input cannot be empty.")


def prompt_action(action, message):
    while True:
        raw = prompt_input(message)

        if raw is None:
            return False
        
        try:
            action(raw.title())
            return True
        
        except ValueError as e:
            print(f"Error: {e}")


# User interaction

cart = ShoppingCart()

while True:
    print("\n1. Add item")
    print("2. Remove item")
    print("3. View cart")
    print("4. Modify item")
    print("5. Checkout")
    print("6. Quit")

    choice = prompt_input("Choose an option: ", allow_blank=True)

    if choice is None:
        print("You are already at the main menu.")
        continue

    if choice == "1":
        item_input = prompt_input("Enter an item to add: ")

        if item_input is None:
            continue

        price = None
        cancelled = False

        while True:
            price_input = prompt_input(
                "Enter the price for the item (press Enter to skip): ", allow_blank=True
            )

            if price_input is None:
                cancelled = True
                break

            if not price_input:
                break

            try:
                price = float(price_input)

                if price < 0:
                    raise ValueError
                break

            except ValueError:
                print("Invalid price. Price must be a non-negative number.")

        if cancelled:
            continue

        try:
            cart.add_item(item_input.title(), price)

        except ValueError as e:
            print(f"Error: {e}")

    elif choice == "2":
        prompt_action(cart.remove_item, "Enter an item to remove: ")

    elif choice == "3":
        cart.view_cart()

    elif choice == "4":
        target_item = prompt_input("Enter the item to modify: ")

        if target_item is None:
            continue

        new_name_input = prompt_input(
            "Enter the new item name (press Enter to keep current): ", allow_blank=True
        )

        if new_name_input is None:
            continue

        cancelled = False
        new_price = None

        while True:
            new_price_input = prompt_input(
                "Enter the new price (press Enter to keep current): ", allow_blank=True
            )

            if new_price_input is None:
                cancelled = True
                break

            if not new_price_input:
                break

            try:
                new_price = float(new_price_input)

                if new_price < 0:
                    raise ValueError
                break

            except ValueError:
                print("Invalid price. Price must be a non-negative number.")

        if cancelled:
            continue

        try:
            new_name = new_name_input.title() if new_name_input else None
            cart.modify_item(target_item.title(), new_name, new_price)

        except ValueError as e:
            print(f"Error: {e}")

    elif choice == "5":
        cart.checkout()

    elif choice == "6":
        print("Goodbye!")
        break
    
    else:
        print("Invalid option. Please choose 1-6. Type 'esc' to cancel a prompt.")

# By Kairos         
    

View Full Source Code →