Help budget app

I can’t find the difference between my chart and the example output

def create_spend_chart(categories):
    line1 = "Percentage spent by category\n"
    line2 = ""
    line4 = ""

    bar_of_zeros = []
    for name in categories:
        percentage = ((name.withdraw_amount / Category.total_with_amt) * 100) // 10 + 1
        zeros = int(percentage) * "o"
        output = zeros.rjust(11)
        bar_of_zeros.append(output)

    for i, nums in enumerate(range(100, -10, -10)):
        line2 += f"{nums:>3}| " + "  ".join([zero[i] for zero in bar_of_zeros]) + "  \n"

    dashes = "-" * (len(categories) * 3 + 1)
    align = len(dashes) + 4
    line3 = dashes.rjust(align) + "\n"

    max_len = max([len(str(category.name)) for category in categories])

    new_catgr_names = []
    for category in categories:
        new_catgr_names.append(str(category.name).capitalize())

    for i in range(max_len):
        nameStr = "     "
        for category in new_catgr_names:
            if i >= len(category):
                nameStr += "   "
            else:
                nameStr += category[i] + "  "
        nameStr += "\n"
        line4 += nameStr

    final_output = line1 + line2 + line3 + line4
    return final_output.rstrip("\n")

Could you show the rest of your code? Considering that this function depends on Category class, helping without having that code might be hard or impossible. It can be in a form of link to code on repl.it.

Here it is, thanks for helping

class Category:
    total_with_amt = 0
    
    def __init__(self, name):
        self.name = name
        self.ledger = []
        self.total_amount = 0
        self.withdraw_amount = 0

    def check_funds(self, amount):
        if amount > self.total_amount:
            return False
        return True

    def deposit(self, amount, description=None):
        if description is not None:
            self.dict = {"amount": amount, "description": description}
        else:
            self.dict = {"amount": amount, "description": ""}
        self.ledger.append(self.dict)
        self.total_amount += amount

    def withdraw(self, amount, description=None):
        yes = self.check_funds(amount)
        if yes:
            if description is not None:
                self.dict = {"amount": -amount, "description": description}
            else:
                self.dict = {"amount": -amount, "description": ""}
            self.ledger.append(self.dict)
            self.total_amount -= amount
            self.withdraw_amount += amount
            Category.total_with_amt += amount
            return True
        return False

    def get_balance(self):
        return self.total_amount

    def transfer(self, amount, category):
        yes = self.check_funds(amount)
        if yes:
            self.dict = {"amount": -amount, "description": "Transfer to " + str(category.name).capitalize()}
            self.ledger.append(self.dict)
            self.total_amount -= amount
            self.withdraw_amount += amount
            Category.total_with_amt += amount

            category.dict = {"amount": amount, "description": "Transfer from " + str(self.name).capitalize()}
            category.ledger.append(category.dict)
            category.total_amount += amount
            return True
        return False

    def __str__(self):
        title = f"{str(self.name).capitalize():*^30}\n"
        events = f""
        for i in range(len(self.ledger)):
            description = self.ledger[i]['description'][:23]
            amount = self.ledger[i]['amount']
            align = 30 - len(str(description)) - len(str(".2f".format(amount))) + 2
            events += f"{description} {amount:>{align}.2f}\n"
        last = f"Total: {self.get_balance():.2f}"
        output = title + events + last

        return output

Consider the implications of using total_with_amt as a class variable. When withdraw method changes Category.total_with_amt this value isn’t changed just for that certain Category class instance (ie. food category), but for the class.

To give visual example what unexpected effects this can have, copy the following line at the end of main.py, just after the main(...) call:

print(create_spend_chart([food, clothing, auto]))

It will print out once more the example chart, although it will look slightly different this time, because in the meantime tests introduces more side-effects.

Keep in mind that this also don’t allow to decide which categories are included in the calculations for the chart, as total amount will include all of them.

@sanity Thank you so much I stop using the class variable and it worked, Thanks!!!