Build a Budget App - Build a Budget App

Tell us what’s happening:

I don’t really understand what the tests are asking of me, nor do I fully understand the output of the F12 console. Also, for some strange reason, while I have checked and double checked that the food category has the appropriate percentage, for some reason, the bar for it is showing a higher percentage, and I’m not sure why.

Your code so far


class Category:
    def __init__(self, name):
        self.name = name
        self.ledger = []
        self.balance = 0 
        
          
    def deposit(self, amount, description = ""):
        self.deposit = amount
        self.description = description
        self.balance += amount
        self.ledger.append({'amount': amount, 'description': description})
        

    def withdraw(self, amount, description = ""):
        self.withdrawal = amount
        self.description = description
        if self.check_funds(amount):
            self.balance -= amount
            self.ledger.append({'amount': -amount, 'description': description})
            return True
        else:
            return False


    def transfer(self, amount, other_category):
        if self.check_funds(amount) == True:
            self.withdraw(amount, f'Transfer to {other_category.name}')
            other_category.deposit(amount, f'Transfer from {self.name}')
            return True
        else:
           return False

    def get_balance(self):
        return self.balance

    def check_funds(self, amount):
        self.amount = amount
        if amount <= self.balance:
            return True
        else:
            return False

    def get_ledger(self):
        return self.ledger
      

    def __str__(self):
        category = self.name
        ledger_title = ""
        ledger_title += f"{category:*^30}"
        ledger = self.ledger
        ledger_sections = ""
        descriptions = []
        amounts = []
        
                
        for i, records in enumerate(ledger):
            descriptions = records["description"]
            amounts = records["amount"]
            if len(descriptions) > 23:
                descriptions = descriptions[0:23]
            amounts = f"{amounts:.2f}"
            descriptions = f"{descriptions:23}"
            ledger_sections += f"{descriptions:.23}" + f"{amounts:>7}" + "\n"

        return f"{ledger_title}\n" + f"{ledger_sections}\nTotal: {self.balance:.2f}\n" 


def create_spend_chart(categories):
    chart = []
    categorylisting = []
    names = ""
    spentAmount = 0
    spendingList = []
    numbers = []
    ledger_amounts = []
    chart_title = "Percentage spent by category\n"
    

    # getting category names
    for category in categories:
        categorylisting.append(category.name)
        names += category.name + " "
        for items in category.ledger:
            if items['amount'] < 0:
                spentAmount += abs(items['amount'])
                spendingList.append(abs(items['amount']))
        
        for items in category.ledger:
            numbers.append(items['amount'])

    totalbalance = sum(numbers)
    
    percentages = [round(num / totalbalance * 100) * 10 for num in spendingList]

    y_axis = ""
    x_axis_line = "    " + "-" * (len(category.name) + 1)


    percentbycategory = dict(zip(categorylisting, percentages))

    for num in range(100, -10, -10):
        if num == 100:
            y_axis += f"{num}|#"
        if num >0 and num < 100:
            y_axis += f" {num}|#"    
        if num == 0:
            y_axis += f"  {num}|#"
     
    y_values = y_axis.split("#")

    for percent in percentbycategory.values():
        if percent == 100:
            y_values[0] += "  o" 
        if percent >=90:
            y_values[1] += "  o" 
        if percent >=80:
            y_values[2] += "  o" 
        if percent >=70:
            y_values[3] += "  o" 
        if percent >=60:
            y_values[4] += "  o" 
        if percent >=50:
            y_values[5] += "  o" 
        if percent >=40:
            y_values[6] += "  o" 
        if percent >=30:
            y_values[7] += "  o" 
        if percent >=20:
            y_values[8] += "  o" 
        if percent >=10:
            y_values[9] += "  o" 
        if percent >=0:
            y_values[10] += "  o" 

    
    y_axis_line = "\n" "".join(y_values)

    max_chars = max(len(category.name) for category in categories)

    
    x_axis = ""
    for i in range(0, max_chars):
        x_axis += "      "
        for category in categories:
            if len(category.name) > i:
                x_axis += category.name[i] + "  "
            else:
                x_axis += "   "
        x_axis += "\n"
            

    chart = f"{chart_title}{y_axis_line}{x_axis_line}\n{x_axis}  "

    return chart
 

food = Category('Food')
food.deposit(900, 'deposit') 
food.withdraw(45.67, 'milk, cereal, eggs, bacon, bread')
clothing = Category('Clothing')
food.transfer(50, clothing)
entertainment = Category('Entertainment')
food.transfer(20, entertainment)
print(food)
print(clothing)
print(entertainment)
spendChart = create_spend_chart([food, clothing, entertainment])
print(spendChart)

The current messages are as follows:

// running tests
16. Printing a Category instance should give a different string representation of the object.
19. The height of each bar on the create_spend_chart chart should be rounded down to the nearest 10.
20. Each line in create_spend_chart chart should have the same length. Bars for different categories should be separated by two spaces, with additional two spaces after the final bar.
21. create_spend_chart should correctly show horizontal line below the bars. Using three - characters for each category, and in total going two characters past the final bar.
23. create_spend_chart chart should have each category name written vertically below the bar. Each line should have the same length, each category should be separated by two spaces, with additional two spaces after the final category.
24. create_spend_chart should print a different chart representation. Check that all spacing is exact. Open your browser console with F12 for more details.
// tests completed
// console output

Your browser information:

User Agent is: Mozilla/5.0 (X11; Linux x86_64; rv:140.0) Gecko/20100101 Firefox/140.0

Challenge Information:

Build a Budget App - Build a Budget App

In your __str__ method, the description isn’t properly left-aligned and you have an extra newline before “Total”, and in your chart function, you’re calculating percentages based on individual withdrawals instead of each category’s total spending - you need to sum all withdrawals per category first, then calculate percentages from those totals.

you need to sum all withdrawals per category first

How? Everything I try results in TypeError because float is not iterable, and neither is int.

You’re getting that error because you’re trying to sum a single number instead of a list - try creating an empty list first, then append each withdrawal amount to it, and finally sum that list.

I swapped out to using math.floor for getting the percentages and the issue with one column bar being taller than it should be has fixed itself.

Now it’s just the spacing issues – and test 16, which is so hopelessly vague that I can’t even begin to make heads nor tails of it.

16. Printing a Category instance should give a different string representation of the object.
19. The height of each bar on the create_spend_chart chart should be rounded down to the nearest 10.
20. Each line in create_spend_chart chart should have the same length. Bars for different categories should be separated by two spaces, with additional two spaces after the final bar.
21. create_spend_chart should correctly show horizontal line below the bars. Using three - characters for each category, and in total going two characters past the final bar.
23. create_spend_chart chart should have each category name written vertically below the bar. Each line should have the same length, each category should be separated by two spaces, with additional two spaces after the final category.
24. create_spend_chart should print a different chart representation. Check that all spacing is exact. Open your browser console with F12 for more details.

Here is my code as it stands right now:

import math
class Category:
    def __init__(self, name):
        self.name = name
        self.ledger = []
        self.balance = 0 
        
          
    def deposit(self, amount, description = ""):
        self.deposit = amount
        self.description = description
        self.balance += amount
        self.ledger.append({'amount': amount, 'description': description})
        

    def withdraw(self, amount, description = ""):
        self.withdrawal = amount
        self.description = description
        if self.check_funds(amount):
            self.balance -= amount
            self.ledger.append({'amount': -amount, 'description': description})
            return True
        else:
            return False


    def transfer(self, amount, other_category):
        if self.check_funds(amount) == True:
            self.withdraw(amount, f'Transfer to {other_category.name}')
            other_category.deposit(amount, f'Transfer from {self.name}')
            return True
        else:
           return False

    def get_balance(self):
        return self.balance

    def check_funds(self, amount):
        self.amount = amount
        if amount <= self.balance:
            return True
        else:
            return False

    def get_ledger(self):
        return self.ledger
      

    def __str__(self):
        category = self.name
        ledger_title = ""
        ledger_title += f"{category:*^30}"
        ledger = self.ledger
        ledger_sections = ""
        descriptions = []
        amounts = []
        
                
        for i, records in enumerate(ledger):
            descriptions = records["description"]
            amounts = records["amount"]
            if len(descriptions) > 23:
                descriptions = descriptions[0:23]
            amounts = f"{amounts:.2f}"
            descriptions = f"{descriptions:23}"
            ledger_sections += f"{descriptions:.23}" + f"{amounts:>7}" + "\n"

        return f"{ledger_title}\n" + f"{ledger_sections}\nTotal: {self.balance:.2f}\n" 


def create_spend_chart(categories):
    chart = []
    categorylisting = []
    names = ""
    spentAmount = 0
    spendingList = []
    numbers = []
    ledger_amounts = []
    category_spending = 0
    spending = []
    chart_title = "Percentage spent by category\n"
    

    # getting category names
    for category in categories:
        categorylisting.append(category.name)
        names += category.name + " "

        for items in category.ledger:
            if items['amount'] < 0:
                spentAmount += abs(items['amount'])
                spendingList.append(abs(items['amount']))
            
        
        for items in category.ledger:
            numbers.append(items['amount'])

    totalbalance = sum(numbers)
    
    percentages = [math.floor(num / totalbalance * 100) * 10 for num in spendingList]

    y_axis = ""
    x_axis_line = "    " + ("-" * 3)*3 + "-" + "  "


    percentbycategory = dict(zip(categorylisting, percentages))

    for num in range(100, -10, -10):
        if num == 100:
            y_axis += f"{num}|#"
        if num >0 and num < 100:
            y_axis += f" {num}|#"    
        if num == 0:
            y_axis += f"  {num}|#"
     
    y_values = y_axis.split("#")

    for percent in percentbycategory.values():
        if percent == 100:
            y_values[0] += " o " 
        if percent >=90:
            y_values[1] += " o " 
        if percent >=80:
            y_values[2] += " o " 
        if percent >=70:
            y_values[3] += " o " 
        if percent >=60:
            y_values[4] += " o " 
        if percent >=50:
            y_values[5] += " o " 
        if percent >=40:
            y_values[6] += " o " 
        if percent >=30:
            y_values[7] += " o " 
        if percent >=20:
            y_values[8] += " o " 
        if percent >=10:
            y_values[9] += " o " 
        if percent >=0:
            y_values[10] += " o " 

    
    y_axis_line = "\n" "".join(y_values)

    max_chars = max(len(category.name) for category in categories)

    
    x_axis = ""
    for i in range(0, max_chars):
        x_axis += "     "
        for category in categories:
            if len(category.name) > i:
                x_axis += category.name[i] + "  "
            else:
                x_axis += "   "
        x_axis += "\n"

            

    chart = f"{chart_title}{y_axis_line}{x_axis_line}\n{x_axis}  "

    return chart
 

food = Category('Food')
food.deposit(900, 'deposit') 
food.withdraw(45.67, 'milk, cereal, eggs, bacon, bread')
clothing = Category('Clothing')
food.transfer(50, clothing)
entertainment = Category('Entertainment')
food.transfer(20, entertainment)
print(food)
print(clothing)
print(entertainment)
spendChart = create_spend_chart([food, clothing, entertainment])
print(spendChart)




And the weird thing is, as near as I can tell, the spacing on one of the issues flagged in the in-browser console should be fine – it looks just like the same thing that it’s telling me it isn’t the same as:

I’m going to try a different browser in case it’s just shenanigans, but I’m starting to wonder if there’s a bug somewhere in the project test code.

In your __str__ method, you need to fix the title formatting to use * for padding and remove the extra newline before “Total”. In your chart function, you need to calculate percentages based on each category’s total spending (not individual withdrawals) and fix the spacing to use " o " for bars and proper dashes for the horizontal line.

16 is fixed, and I think the dashed bar is fixed as well.

I’m still at a loss with regard to 19, 20, 23, and 24.

Here’s my code so far – I’ve added some printouts of the numbers I’m seeing after attempting to do the percentages based on each category’s total spending, because the math, as they say, is not mathing. I’ve rebuilt how my chart is constructed to see if I could get around the issues in 19 and 20, but alas.

import math
class Category:
    def __init__(self, name):
        self.name = name
        self.ledger = []
        self.balance = 0 
        self.withdrawal = 0
        self.deposited = 0
        
          
    def deposit(self, amount, description = ""):
        self.deposit = amount
        self.description = description
        self.balance += amount
        self.deposited += amount
        self.ledger.append({'amount': amount, 'description': description})
        

    def withdraw(self, amount, description = ""):
        self.description = description
        if self.check_funds(amount):
            self.balance -= amount
            self.withdrawal += amount
            self.ledger.append({'amount': -amount, 'description': description})
            return True
        else:
            return False


    def transfer(self, amount, other_category):
        if self.check_funds(amount) == True:
            self.withdraw(amount, f'Transfer to {other_category.name}')
            other_category.deposit(amount, f'Transfer from {self.name}')
            return True
        else:
           return False

    def get_balance(self):
        return self.balance

    def check_funds(self, amount):
        self.amount = amount
        if amount <= self.balance:
            return True
        else:
            return False

    def get_ledger(self):
        return self.ledger
      

    def __str__(self):
        category = self.name
        ledger_title = self.name.center(30, "*")
        ledger = self.ledger
        ledger_sections = ""
        descriptions = []
        amounts = []
        
                
        for i, records in enumerate(ledger):
            descriptions = records["description"]
            amounts = records["amount"]
            if len(descriptions) > 23:
                descriptions = descriptions[0:23]
            amounts = f"{amounts:.2f}"
            descriptions = f"{descriptions:.23}"
            ledger_sections += f"{descriptions:23}{amounts:>7}\n"

        return f"{ledger_title}\n{ledger_sections}Total: {self.balance:.2f}" 


def create_spend_chart(categories):
    chart = []
    categorylisting = []
    names = ""
    spentAmount = 0
    spendingList = []
    numbers = []
    categoryspending = []
    deposits = []
    y_axis = "Percentage spent by category\n"
    

    # getting category names
    for category in categories:
        categoryspent = 0
        categorylisting.append(category.name)
        names += category.name + " "
        categoryspending.append(category.withdrawal)
        categoryspent = math.floor(sum(categoryspending))

        

        for items in category.ledger:
            numbers.append(items['amount'])
            if items['amount'] > 0:
                deposits.append(items['amount'])
            if items['amount'] < 0:
                spentAmount += abs(items['amount'])
                spendingList.append(abs(items['amount']))


    totalbalance = sum(numbers)
    print("-----------")
    print("Printouts for reference:")
    print("\n")
    print("Total balance:", totalbalance)
    print("\n")
    print("Spent amount:", f"{spentAmount:.2f}")
    print("\n")
    print("Category spending:", categoryspending)
    print("\n")
    percentages = []

    percentages = [round((num / spentAmount)*100, -1) for num in categoryspending]

    print("Percentages:", percentages)
    print("\n")
    percentbycategory = dict(zip(categorylisting, percentages))
    print("Percent by category:", percentbycategory)

    print("\n")
    print("---------")
    print("\n")
    for i in range(100, -10, -10):
        if i == 100:
            y_axis += f"{i}| "
        if i >0 and i < 100:
            y_axis += f" {i}| "    
        if i == 0:
            y_axis += f"  {i}| "
        for percent in percentages:
            y_axis += "o  " if percent >= i else " "

        y_axis += " \n"
        y_axis_line = ""
        y_axis_line += y_axis

    x_axis_line = "    " + "-" *(3*len(categories) + 1)

    max_chars = max(len(category.name) for category in categories)

    
    x_axis = "     "
    for i in range(0, max_chars):
        for category in categories:
            if len(category.name) > i:
                x_axis += category.name[i] + "  "
            else:
                x_axis += "   "
                
        x_axis += "\n   " + "  "

            

    chart = f"{y_axis}{x_axis_line}\n{x_axis}"
    


    return chart
 

food = Category('Food')
food.deposit(900, 'deposit') 
food.withdraw(45.67, 'milk, cereal, eggs, bacon, bread')
clothing = Category('Clothing')
food.transfer(50, clothing)
clothing.withdraw(25, 'Pair of jeans')
entertainment = Category('Entertainment')
food.transfer(20, entertainment)
entertainment.withdraw(10, 'DVD')
print(food)
print(entertainment)
print(clothing)
spendChart = create_spend_chart([food, clothing, entertainment])
print(spendChart)

And the readout for the printouts:


Printouts for reference:

Total balance: 819.33

Spent amount: 150.67

Category spending: [115.67, 25, 10]

Percentages: [80.0, 20.0, 10.0]

Percent by category: {‘Food’: 80.0, ‘Clothing’: 20.0, ‘Entertainment’: 10.0}


The assertion error and diff gives you a lot of information to track down a problem. For example:

AssertionError: 'Year' != 'Years'
- Year
+ Years
?     +

Your output comes first, and the output that the test expects is second.

AssertionError: ‘Year’ != ‘Years’

Your output: Year does not equal what’s expected: Years

This is called a diff, and it shows you the differences between two files or blocks of code:

- Year
+ Years
?     +

- Dash indicates the incorrect output
+ Plus shows what it should be
? The Question mark line indicates the place of the character that’s different between the two lines. Here a + is placed under the missing s .

Here’s another example:

E       AssertionError: Expected different output when calling "arithmetic_arranger()" with ["3801 - 2", "123 + 49"]
E       assert '  3801      123    \n   - 2     + 49    \n------    -----    \n' == '  3801      123\n-    2    +  49\n------    -----'
E         -   3801      123
E         +   3801      123    
E         ?                ++++
E         - -    2    +  49
E         +    - 2     + 49    
E         - ------    -----
E         + ------    -----    
E         ?                +++++

The first line is long, and it helps to view it as 2 lines in fixed width characters, so you can compare it character by character:

'  3801      123    \n   - 2     + 49    \n------    -----    \n'
'  3801      123\n-    2    +  49\n------    -----'

Again, your output is first and the expected output is second. Here it’s easy to see extra spaces or \n characters.

E         -   3801      123
E         +   3801      123    
E         ?                ++++

Here the ? line indicates 4 extra spaces at the end of a line using four + symbols. Spaces are a little difficult to see this way, so it’s useful to use both formats together.

I hope this helps interpret your error!

Finally got it sorted. It was a case of chasing down where spaces needed to be added, or in some cases removed, and figuring out a way of avoiding having a new line (“\n”) at the end of last category.

Thank you to those who helped. Some of the error messages were difficult to understand, and it might be worth tweaking them (especially test 16’s message, which is incredibly vague and unhelpful) to make them more helpful.

This topic was automatically closed 28 days after the last reply. New replies are no longer allowed.