Build a Budget App

Tell us what’s happening:

My create_spend_chart(categories) function is failing your tests, and I cannot figure out why. Running the code from the command line is working as you can see in the figure below.

Please help. Thanks.

Your code so far

class Category:
    def __init__(self, name):
        self.name = name
        self.ledger = []
    
    def deposit(self, amount, description = ''):
        self.ledger.append({'amount' : amount, 'description': description})
    
    def withdraw(self, amount, description = ''):
        if self.check_funds(amount):
            self.ledger.append({'amount' : -amount, 'description': description})
            return True
        return False
    
    def get_balance(self):
        balance = 0
        for entry in self.ledger:
            # ignore withdrawals that make balance < 0
            amount, description = list(entry.keys())
            if entry[amount] >= 0 or balance + entry[amount] >= 0:
                balance += entry[amount]
        return balance
    
    def transfer(self, amount, category):
        if self.check_funds(amount):
            entry = {'amount' : -amount, 'description': f'Transfer to {category.name}'}
            self.ledger.append(entry)
            entry = {'amount' : amount, 'description': f'Transfer from {self.name}'}
            category.ledger.append(entry)
        return self.check_funds(amount)
    
    def check_funds(self, amount):
        balance = self.get_balance()
        return balance >= amount
    
    def __str__(self):
        title_len = 30
        title_head_len = (title_len - len(self.name)) // 2
        title_tail_len = title_len - title_head_len - len(self.name)
        title = f"{'*' * title_head_len}{self.name}{'*' * title_tail_len}"
        result = title
        for entry in self.ledger:
            amount, description = list(entry.keys())
            description_str = entry[description][:23]
            amount_str = f"{entry[amount]:.2f}"
            spaces_str = ' ' * (title_len - len(description_str) - len(amount_str))
            entry_str = f"\n{description_str}{spaces_str}{amount_str}"
            result += entry_str
        result += f"\nTotal: {self.get_balance()}"
        return result

def create_spend_chart(categories):
    # Sort withdrawals in descending order
    withdrawals = []
    for entry in categories.ledger:
        amount, description = list(entry.keys())
        if entry[amount] < 0:
            if not len(withdrawals):
                withdrawals.append((-entry[amount], entry[description]))
            else:
                for i in range(len(withdrawals)):
                    if (-entry[amount] > withdrawals[i][0]):
                        withdrawals.insert(i, (-entry[amount], entry[description]))
                        break
                else:
                    withdrawals.append((-entry[amount], entry[description]))
    
    total = 0
    for w in withdrawals:
        total += w[0]
    total = round(total)
    
    chart = []
    for w in withdrawals:
        chart.append(((round(100 * w[0] / total) // 10) * 10, w[1]))
    
    result = 'Percentage spent by category\n'
    line_width = 4 + len(chart) * 3 + 1
    chart_lines = []
    for p in range(100, -1, -10):
        percentage_prefix_str = '' if p == 100 else ('  ' if p == 0 else ' ')
        line = f"{percentage_prefix_str}{p}|"
        first_o = True
        for percentage, _ in chart:
            if percentage >= p:
                if first_o:
                    line += ' o'
                    first_o = False
                else:
                    line += '  o'
        if len(line) < line_width:
          line += f"{' ' * (line_width - len(line))}"
        chart_lines.append(line)
    for line in chart_lines:
        result += f"{line}\n"
    horiz_line = f"{' ' * 4}{'-' * (3 * len(chart) + 1)}\n"
    result += horiz_line
    descriptions = []
    descriptions_lines_lens = []
    for w in withdrawals:
        descriptions.append([c for c in w[1]])
        descriptions_lines_lens.append(len(w[1]))
    description_suffix = f"{' ' * (line_width - len(horiz_line))}"
    description_lines_num = max(*descriptions_lines_lens)
    for i in range(description_lines_num):
        description_line = f"{' ' * 5}"
        for j, d in enumerate(descriptions):
            description_prefix = f"{'  ' if j > 0 else ''}"
            description_line += f"{description_prefix}{d[i] if i < len(d) else ' '}"
        result += f"{description_line}{description_suffix}  \n"
    result = result[:-1]
    return result


food = Category('Food')
food.deposit(1000, 'initial deposit')
food.withdraw(10.15, 'groceries')
food.withdraw(15.89, 'restaurant and more food for dessert')
clothing = Category('Clothing')
food.transfer(50, clothing)
print(create_spend_chart(food))

Lesson URL is https://www.freecodecamp.org/learn/python-v9/lab-budget-app/build-a-budget-app

Hi @rbuhescu

Percentage spent by category
100|          
 90|          
 80|          
 70|          
 60| o        
 50| o        
 40| o        
 30| o        
 20| o  o     
 10| o  o  o  
  0| o  o  o  
    ----------
     F  C  A  
     o  l  u  
     o  o  t  
     d  t  o  
        h     
        i     
        n     
        g     

“Clothing” is a category. “Transfer to Clothing” is not a category.

Happy coding