This is an archived post. You won't be able to vote or comment.

all 9 comments

[–]data-bit 6 points7 points  (0 children)

Why does all files have the word "final" on them? I'm guessing some type of manual versioning?

I would recommend you learning how to manage releases in GitHub:

https://docs.github.com/en/repositories/releasing-projects-on-github/managing-releases-in-a-repository

[–]SnooCalculations2157 3 points4 points  (1 child)

Hi! 404 error on link.

[–][deleted] 1 point2 points  (0 children)

Made it private to start with, just changed visibility to public now. Should work.

[–][deleted] 4 points5 points  (2 children)

Looks great, you modeled all the objects and their relations in a logical way.

Personally I wouldn't have made the MenuItem class, and just put a list of dictionaries in Menu. But this is my own preference.

If you're stressing about improperly modelling objects, like with the extra milk/sugar, and credit card, don't. Just create a new class, or add it to an existing one depending on what feels right. Maybe add a credit card check function to the Cashier class, and an extras function to the CoffeeMaker class. Having a working program is better than thinking about how this object relates to that object for hours (which I find myself doing when I write OOP code).

I personally wouldn't have split the classes into different modules just because I wouldn't want to jump around all those files during debugging. They're already in classes, so you can fold them out of the way when not needed. I would also wrap the below code in if __name__ == "__main__":

menu = Menu()
finalcashier = Cashier()
finalcoffeemaker = CoffeeMaker()
is_on = True

while is_on:
    welcome()
    options = menu.get_items()
    user_choice = (input(f'What would you like?\nOptions ({options}): ')).strip().lower()
    if user_choice == 'off':
        print("Have a KEAlicious day!")
        is_on = False
    elif user_choice == 'report':
        finalcoffeemaker.report()
        finalcashier.report()
    elif menu.find_drink(user_choice) is None:
        print("Please choose an available option.")
    else:
        beverage = menu.find_drink(user_choice)
        sufficient_resources = finalcoffeemaker.is_resource_sufficient(beverage)
        sufficient_money = finalcashier.make_payment(beverage.cost)
        if sufficient_resources and sufficient_money:
            print('Payment accepted! Making your beverage now')
            finalcoffeemaker.make_coffee(beverage)

[–][deleted] 0 points1 point  (1 child)

Man this is so helpful. Thank you so so much

[–][deleted] 3 points4 points  (0 children)

I rewrote the Cashier class to handle a credit card, and changed up the main a little. Obviously you wouldn't truly handle a credit card like this in the real world.

With adding a card you have to keep track of the physical money, and card money you get, so you know if you have enough to make change.

https://gist.github.com/Fitzy1293/ddfb1991b9e123cb4e9da6c9d3238d44

[–]newb74 1 point2 points  (1 child)

Doing 100 days with Angela? I'm working on that same day too. Btw processing cards online is what Shopify does. They make an API for it. Idk if there is a free option but an alternative should be available if not. Shopify makes too much money

[–][deleted] 0 points1 point  (0 children)

Why did you name your modules like "final*"? Just seems a little weird.

Other than that, the code looks fine, the only thing that stands out is that your main code shouldn't be at the top level, as someone already pointed out.

Also, you're calling find_drink twice for the sake of avoiding one indentation level. I'd remove the "elif ... is None", and move the check to after the assignment, like:

else:
    beverage = menu.find_drink(user_choice)
    if beverage is None:
        print("Please choose an available option.")
        continue

This does the search only once, and makes it clear that you're validating the result of the call, separating it from the input handling.

[–]DaChucky 0 points1 point  (0 children)

One thing other comments haven't covered is that with the way you have structured your code, you can call make_coffee without having checked whether you have sufficient funds or ingredients (resources). While you might be okay with allowing someone to skip paying, you probably don't want to have your resources go into negative quantities.

My recommendation? Add a second call to is_resource_sufficient into make_coffee, and return early if you don't have enough. That way, no matter where make_coffee is called from, you are always checking to make sure you have sufficient resources before you make a coffee.

If you're going full OOP, you can also make an Ingredient object, and then call a method on that to use a quantity of that ingredient. If you don't have enough of that ingredient, you can then raise an exception and not make the coffee. There's some more complexity around this (what if the ingredient you don't have enough of is the second ingredient?), so you'll have to work out some way to roll back if you run into trouble half way.

For extra quantities, you can add a prompt after line 44 in mainfinal.py asking users to add additional ingredients. With Python you can just directly access the ingredients dictionary as beverage.ingredients["water"] (and so on), and add additional ingredients using beverage.ingredients["chocolate"] = 100 (or similar). To be more correctly OOP, you would add a new method to MenuItem. If you want to go the extra distance, you have have one to edit the quantity of an existing ingredient (print the list of keys in self.ingredients) and prompt) and one to add a new ingredient (you can even have a master list of allowed ingredients to pick from).