Build a Budget App - Build a Budget App Test 19 Fails

Tell us what’s happening:

Test #19 fails: The rounding is not passing even though the output on the graph seems correct as are the returned percentages in the output. Tests 20, 23 and 24 also fail, but that is a different issue, probably related to spacing, which I will look into in greater detail on my own before asking for help on that.

Output

Your code so far

def create_spend_chart(categories):
    bar_chart_str = 'Percentage spent by category\n'
    
    total_withdrawals = 0
    # [{category.name: category_withdrawal},...]
    category_withdrawals = []
    for category in categories:
        category_withdrawal = 0
        for ledger_dict in category.ledger:
            if ledger_dict['amount'] < 0:
                total_withdrawals -= ledger_dict['amount']
                category_withdrawal -= ledger_dict['amount']
        category_withdrawals.append({category.name: category_withdrawal})

    category_withdrawals = sorted(category_withdrawals, 
                                    key=lambda d: list(d.values())[0],
                                    reverse=True)
    category_names = [list(d.keys())[0] for d in category_withdrawals]
    print(category_names)
    category_amounts = [list(d.values())[0] for d in category_withdrawals]
    print(category_amounts)
    category_pcts = [math.floor(10*num/total_withdrawals)*10 for num in category_amounts]
    print(category_pcts)    
    for i in range(100, -10, -10):
        y_index = (str(i)+'|').rjust(4)
        bar_chart_str += y_index
        for pct in category_pcts:
            if i <= pct:
                bar_chart_str += ' o '
        bar_chart_str += '\n'
    
    num_categories = len(category_names)
    x_axis = ' '*4
    for i in range(num_categories):
        x_axis += '---'
    x_axis += '-'
    bar_chart_str += x_axis + '\n'
    left_pad = ' '*4
    longest_cat_name = 0
    for name in category_names:
        if len(name) > longest_cat_name:
            longest_cat_name = len(name)
    category_letters = [list(name) for name in category_names]
    #print(category_letters)
    for row in range(longest_cat_name):
        bar_chart_str += left_pad
        for col in range(num_categories):
            len_current_cat = len(category_letters[col])
            if row < len_current_cat:
                bar_chart_str += f' {category_letters[col][row]} '
                if col == len(category_letters) - 0:
                    bar_chart_str += ' '    
        bar_chart_str += '\n'
    bar_chart_str = bar_chart_str.rstrip('\n')      
    return bar_chart_str

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

clothing = Category('Clothing')
clothing.deposit(500, 'deposit')
clothing.withdraw(100, 'suit')

auto = Category('Auto')
auto.deposit(10000, 'deposit')
auto.withdraw(50, 'maintainance')

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

Your browser information:

User Agent is: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36 Edg/143.0.0.0

Challenge Information:

Build a Budget App - Build a Budget App

Could you share the Category class as well? It’s needed to accurately reproduce the state during tests.

1 Like

I tried to edit the OP to include it but it didn’t work so I’ll post the top part of the code over here.

import math

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
        else:
            return False
    
    def get_balance(self):
        total = 0
        for ledger_dict in self.ledger:
            total += ledger_dict['amount']
        return total

    def transfer(self, amount, destination):
        description = f'Transfer to {destination.name}'
        description2 = f'Transfer from {self.name}'

        if self.check_funds(amount):
            self.ledger.append({'amount': -amount, 'description': description})
            destination.ledger.append({'amount': amount, 'description': description2})
            return True
        else:
            return False

    def check_funds(self, amount):
        return amount <= self.get_balance()

    def __str__(self):
        title = self.name
        total = 0
        padded_title = title.center(30, '*')
        return_str = padded_title + '\n'
        for ledger_dict in self.ledger:            
            if len(ledger_dict['description']) > 23:
                padded_description = ledger_dict['description'][:23]
            else:
                padded_description = ledger_dict['description'].ljust(23)
            if len(str(ledger_dict['amount'])) > 7:
                amount_str = str(ledger_dict['amount'])[:7]
            else:
                amount_str = str(ledger_dict['amount']).rjust(7)

            return_str += padded_description + f'{float(amount_str):>{7}.{2}f}' + '\n'
            total += ledger_dict['amount']
        return_str += f'Total: {total:.2f}'
        return return_str 

You are making the correct calculation, but the test is expecting spaces to fill out the empty areas of the chart, so it’s not correctly interpreting exactly where your bars are.

Check the console output in F12.

If I replace all spaces with stars:

Percentage*spent*by*category
100|
*90|
*80|
*70|*o*
*60|*o*

should look more like

Percentage*spent*by*category
100|******
*90|******
*80|******
*70|*o****
*60|*o****
*50|*o****

So I think it’s related to the spacing problem you’ve mentioned. Try to sort that out and come back to this test if it’s still a problem.

On second look:

Percentage*spent*by*category
100|
*90|
*80|
*70|*o*
*60|*o*
*50|*o*
*40|*o*
*30|*o*
*20|*o**o*
*10|*o**o*
**0|*o**o*

It might be this extra space after the o causing the problem

Try it like this?

Percentage*spent*by*category
100|
*90|
*80|
*70|*o
*60|*o
*50|*o

This is how my output looks like if I use replace method to get rid of empty spaces with *. I guess I should continue adding empty spaces so that all the lines have the same length.

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

I thought the point was to have the same length in each line of the graph so something like this. Now test 20 passes but 19, 23 and 24 still fail.

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

My complete code in the present moment looks like this.

import math

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
        else:
            return False
    
    def get_balance(self):
        total = 0
        for ledger_dict in self.ledger:
            total += ledger_dict['amount']
        return total

    def transfer(self, amount, destination):
        description = f'Transfer to {destination.name}'
        description2 = f'Transfer from {self.name}'

        if self.check_funds(amount):
            self.ledger.append({'amount': -amount, 'description': description})
            destination.ledger.append({'amount': amount, 'description': description2})
            return True
        else:
            return False

    def check_funds(self, amount):
        return amount <= self.get_balance()

    def __str__(self):
        title = self.name
        total = 0
        padded_title = title.center(30, '*')
        return_str = padded_title + '\n'
        for ledger_dict in self.ledger:            
            if len(ledger_dict['description']) > 23:
                padded_description = ledger_dict['description'][:23]
            else:
                padded_description = ledger_dict['description'].ljust(23)
            if len(str(ledger_dict['amount'])) > 7:
                amount_str = str(ledger_dict['amount'])[:7]
            else:
                amount_str = str(ledger_dict['amount']).rjust(7)

            return_str += padded_description + f'{float(amount_str):>{7}.{2}f}' + '\n'
            total += ledger_dict['amount']
        return_str += f'Total: {total:.2f}'
        return return_str 
            
def create_spend_chart(categories):
    bar_chart_str = 'Percentage spent by category\n'
    
    total_withdrawals = 0
    # [{category.name: category_withdrawal},...]
    category_withdrawals = []
    for category in categories:
        category_withdrawal = 0
        for ledger_dict in category.ledger:
            if ledger_dict['amount'] < 0:
                total_withdrawals -= ledger_dict['amount']
                category_withdrawal -= ledger_dict['amount']
        category_withdrawals.append({category.name: category_withdrawal})

    category_withdrawals = sorted(category_withdrawals, 
                                    key=lambda d: list(d.values())[0],
                                    reverse=True)
    category_names = [list(d.keys())[0] for d in category_withdrawals]
    print(category_names)
    category_amounts = [list(d.values())[0] for d in category_withdrawals]
    print(category_amounts)
    category_pcts = [math.floor(10*num/total_withdrawals)*10 for num in category_amounts]
    print(category_pcts)    
    for i in range(100, -10, -10):
        current_line = ""
        y_index = (str(i)+'|').rjust(4)
        current_line += y_index
        for pct in category_pcts:
            if i <= pct:
                current_line += ' o '
        current_line = current_line.ljust(4 + 3*len(categories)+1)
        bar_chart_str += current_line + '\n'
    
    num_categories = len(category_names)
    x_axis = ' '*4
    for i in range(num_categories):
        x_axis += '---'
    x_axis += '-'
    bar_chart_str += x_axis + '\n'
    left_pad = ' '*4
    longest_cat_name = 0
    for name in category_names:
        if len(name) > longest_cat_name:
            longest_cat_name = len(name)
    category_letters = [list(name) for name in category_names]
    
    for row in range(longest_cat_name):
        current_line = left_pad
        for col in range(num_categories):
            len_current_cat = len(category_letters[col])
            if row < len_current_cat:
                current_line += f' {category_letters[col][row]} '
                if col == len(category_letters) - 0:
                    current_line += ' '    
        current_line = current_line.ljust(4 + 3*len(categories)+1) 
        bar_chart_str += current_line + '\n'
    bar_chart_str = bar_chart_str.rstrip('\n')      
    return bar_chart_str

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

clothing = Category('Clothing')
clothing.deposit(500, 'deposit')
clothing.withdraw(100, 'suit')

auto = Category('Auto')
auto.deposit(10000, 'deposit')
auto.withdraw(50, 'maintainance')

categories = [food, clothing, auto]
bar_chart = create_spend_chart(categories)
bar_chart = bar_chart.replace(' ', '*')
print(bar_chart)

opening the browser console gives useful infos

AssertionError: 'Perc[74 chars] 70| o        \n 60| o        \n 50| o        [300 chars]    ' 
             != 'Perc[74 chars] 70|    o     \n 60|    o     \n 50|    o     [300 chars] t  '

you have issues with the column values

why are you sorting?

It is a data visualization best practice that a bar chart’s categories are ordered from largest to smallest which is why I sorted the categories. I come from a data analytics background if you’re curious. Which is also why I am confused on how browser code inspection works. Guess I’ll have to watch a tutorial on how to master that skill. But for now I will get rid of sorting in my code and see if the tests pass.

Best to focus on the instructions here and only do what’s asked.

If you do extra operations or changes it usually will confuse the tests.

this is not a general thing for browser code inspection. The tests in this project leave extra output in the browser console. Below how to read that.

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!

1 Like