Instructions
Requirements and Specifications
Source Code
#
# How this program can be improved?
# Using a list is too difficult to manage because we have to like store
# different kinds of data on each index. There must be a better way to
# organize the data like using a dictionary or a class.
#
# Constant variables for accessing a product information from a list
PRODUCT_NAME = 0
PRODUCT_STOCK = 1
PRODUCT_PRICE = 2
# Constant variables for accessing customer information from a list
CUSTOMER_NAME = 0
CUSTOMER_PURCHASES = 1
# Constant variables for accessing a purchase of a customer on its list of
# purchased products
PURCHASED_PRODUCT = 0
PURCHASED_QUANTITY = 1
def init_default_products():
"""
Create the default products upon run of program
:return: List of products
"""
# This is going to be a list of list. Each item in the list is a product
# represented as a list which holds the name, stock, and price
products = [
["Cup", 5, 4.99],
["Plate", 5, 10.00],
["Bowl", 5, 7.50],
["Spoon", 5, 1.99],
["Fork", 5, 1.00],
["Air", 5, 0],
["Python", 1, -1]
]
return products
def find_product(products, name):
"""
Find the product given a name
:param products: List of products to be searched
param name: Target name
:return: Found product
"""
for product in products:
if product[PRODUCT_NAME] == name:
return product
return None
def find_customer(customers, name):
"""
Find the customer given a name
:param customers: List of customers to be searched
:param name: Target name
:return: Found customer
"""
for customer in customers:
if customer[CUSTOMER_NAME] == name:
return customer
return None
def add_new_purchase(products, customers):
"""
Module that allows to enter a new single purchase for a customer.
:param products: Reference for purchasing a product
:param customers: Where to add a new customer if new, otherwise reference for discount.
:return: None
"""
# Ask for the customer's name, abort if no customer name is applied
customer_name = input("Enter customer name: ").strip()
if customer_name == "":
print("A customer name is required.")
return
# Ask for the product, abort if not given
product_name = input("Enter product name: ").strip()
if product_name == "":
print("A product name is required.")
return
# Check that the product exists, abort if not found
product = find_product(products, product_name)
if product is None:
print("The product does not exist.")
return
# Abort if product has no price
if product[PRODUCT_PRICE] < 0:
print("The product cannot be sold because there is no price.")
return
# Ask for the quantity, abort if it is 0 or negative
quantity = int(input("Enter quantity: "))
if quantity <= 0:
print("A positive quantity is required.")
return
# Check that there is enough stock for the quantity required, abort if not
if quantity > product[PRODUCT_STOCK]:
print("The product does not have enough stock for the given quantity.")
return
# Calculate the total cost, add 10% discount if it was a previous customer
total_cost = product[PRODUCT_PRICE] * quantity
customer = find_customer(customers, customer_name)
if customer is None:
# A new customer cannot order a free product
if product[PRODUCT_PRICE] == 0:
print("New customers cannot order free products.")
return
# For new customer we will create a new record for it
# Index 0 is the name while Index 1 is the list of products they purchased
customer = [customer_name, []]
customers.append(customer)
else:
# Apply 10% discount
total_cost -= (total_cost * 0.10)
product[PRODUCT_STOCK] -= quantity
print(customer_name + " purchased " + str(quantity) + " x " + str(product_name))
print("Unit price: $" + "{:.2f}".format(product[PRODUCT_PRICE]))
print("Total price: $" + "{:.2f}".format(total_cost))
# Update customer product purchase history
customer[CUSTOMER_PURCHASES].append([product, quantity])
def update_products(products):
"""
Allow the user to set new products for sale
:param products: List of products to be reset
:return: None
"""
# Get the product names, abort if none provided
names = input("Enter product names separate it by space: ").strip()
if names == "":
print("Product names are required.")
return
# Get the prices, abort if none provided
prices = input("Enter the prices separate it by space: ").strip()
if prices == "":
print("The prices are required.")
return
# Get the stocks, abort if none provided
stocks = input("Enter the stock quantities separate it by space: ").strip()
if stocks == "":
print("The stock quantities are required.")
return
# Break the names, prices, and stocks into lists. Make sure that
# They are of the same lengths
names = names.split()
prices = prices.split()
stocks = stocks.split()
if len(names) != len(prices) or len(names) != len(stocks):
print("Invalid input. Either a name, price and/or quantity did not align with the input.")
print("If a product has no price, then enter a non positive value.")
return
# Re-create the products
products.clear()
for i in range(len(names)):
products.append([names[i], int(stocks[i]), float(prices[i])])
def calculate_customer_total_purchase_cost(customer):
"""
Total the purchase cost of a customer based on products it purchased
:param customer: Customer to be processed
:return: Total purchase cost
"""
total_cost = 0
for purchase in customer[CUSTOMER_PURCHASES]:
total_cost += purchase[PURCHASED_PRODUCT][PRODUCT_PRICE] * purchase[PURCHASED_QUANTITY]
return total_cost
def find_most_valuable_customer(customers):
"""
Display the most valuable customer which is the customer with the most
highest amount of total purchase.
:param customers: List of customers for search
:return: None
"""
most_valuable_customer = None
# Search each customer and compare it with one other to find the most
# with purchase cost
for customer in customers:
if most_valuable_customer is None or calculate_customer_total_purchase_cost(customer) \
> calculate_customer_total_purchase_cost(most_valuable_customer):
most_valuable_customer = customer
if most_valuable_customer is None:
print("No valuable customer found.")
return
print("The most valuable customer is '" + most_valuable_customer[CUSTOMER_NAME] + "'")
def replenish_products(products):
"""
Reset the stock quantities for all products
:param products: Products to be processed
:return: None
"""
# Ask for the starting quantity, abort if invalid
stocks = int(input("Enter the starting quantities of products: "))
if stocks < 0:
print("Invalid quantity, it should be a zero or positive value.")
return
for product in products:
product[PRODUCT_STOCK] = stocks
def display_customers(customers):
"""
Display all customers
:param customers: List of customers for display
:return: None
"""
print("Existing Customers:")
for customer in customers:
print(" " + customer[CUSTOMER_NAME])
def display_products(products):
"""
Display all products
:param products: List of products for display
:return: None
"""
print("Name".ljust(20) + "Price".rjust(10) + "Stock".rjust(10))
# Display each product's info
for product in products:
print(product[PRODUCT_NAME].ljust(20), end="")
if product[PRODUCT_PRICE] > 0:
print("${:.2f}".format(product[PRODUCT_PRICE]).rjust(10), end="")
elif product[PRODUCT_PRICE] == 0:
print("FREE".rjust(10), end="")
else:
print("UNKNOWN".rjust(10), end="")
print(str(product[PRODUCT_STOCK]).rjust(10))
def count_purchased_product(customer, product_name):
"""
Count how many times a customer purchased a product
"""
count = 0
for purchase in customer[CUSTOMER_PURCHASES]:
if purchase[PURCHASED_PRODUCT][PRODUCT_NAME] == product_name:
count += purchase[PURCHASED_QUANTITY]
return count
def display_customer_and_products_table(products, customers):
"""
Display a table of products and the customers as a 2 dimensional table
showing the quantities purchased for each customer on a product
:param products: List of products
:param customers: List of customers
:return: None
"""
# Print out a header
print("".ljust(20), end="")
for product in products:
print(product[PRODUCT_NAME].ljust(15), end="")
print()
# Print out the customers and purchases for each product
for customer in customers:
print(customer[CUSTOMER_NAME].ljust(20), end="")
# For each customer count the total quantity of products
for product in products:
count = count_purchased_product(customer, product[PRODUCT_NAME])
print(str(count).ljust(15), end="")
print()
def main():
"""
This will be our entry point of the program to handle the main control
flow of logic.
:return: None
"""
# Start with default products
products = init_default_products()
# Start with an empty customers
customers = []
while True:
# Display a menu that allows the user to perform command
print("What would you like to do?")
print("1. Add a new purchase")
print("2. Set a new list of products for purchase")
print("3. Replenish products")
print("4. Display customers")
print("5. Display products")
print("6. Find most valuable customer")
print("7. Display customer and product table")
print("0. Exit")
option = int(input("Option: "))
if option == 1:
add_new_purchase(products, customers)
elif option == 2:
update_products(products)
elif option == 3:
replenish_products(products)
elif option == 4:
display_customers(customers)
elif option == 5:
display_products(products)
elif option == 6:
find_most_valuable_customer(customers)
elif option == 7:
display_customer_and_products_table(products, customers)
elif option == 0:
break
print()
# Start the program
main()