Build a Budget App - Build a Budget App - Issues with percentage chart (steps 19, 20, 23 and 24)

Tell us what’s happening:

I have been working on this for a week but I am still stuck on solving steps 19, 20, 23 and 24.

To me, I rounded the bars columns to the nearest ten using round() and -1, but for some reasons it does not pass. I tried using F12, but its the first time I use it and have no clue how it works

Your code so far

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

    # the string value of the object
    def __str__(self):
        # title part
        full_display = ''

        empt = ' '
        dot = '*'
        dot_1 =  round((30 - len(self.name)) / 2)
        dot_2 = 30 - len(self.name) - dot_1
        title = f'{dot_1 * dot}{self.name}{dot_2 * dot}\n'

        full_display += title
        

        # list part
        # first, we reach each dict
        for transaction in self.ledger:

            descrip = transaction['description']

        # I don't believe it was really needed, but it works
            initial_value = (transaction['amount'])

            value = f"{initial_value: .2f}" 
            # as long as it works...

            if len(descrip) > 23:
                descrip = descrip[:23]
                space = 30 - len(descrip) -len(str(value))

                full_display += (f'{descrip}{space*empt}{value}\n')
            else:
                space = 30 - len(descrip) -len(str(value))

                full_display += (f'{descrip}{space*empt}{value}\n')

        # out of for loop
        full_display += f'Total: {self.balance}'
        
        # It will return the full list of transactions, transfers and current balance
        return full_display

    def deposit(self, amount, description = ''):

        self.balance += amount
        self.ledger.append({
            'amount': amount,
            'description': description
        })

    
    def withdraw(self, amount, description = ''):
        if self.check_funds(amount):
            self.balance -= amount 

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


    def get_balance(self):
        return self.balance

    def transfer(self, amount, destination):

        if self.check_funds(amount):
            self.withdraw(amount, f'Transfer to {destination.name}')

            destination.deposit(amount, f'Transfer from {self.name}')
            
            return True

        else:
            return False
    

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

# end of category class


def create_spend_chart(categories):
    full_chart = ''
    chart_title = 'Percentage spent by category\n'
    space =' '

    full_chart += chart_title

    withdraw_per_category = []
    for category in categories:
        withdraw_category = 0 # it reset for each category

        for transaction in category.ledger:
            initial_value = (transaction['amount'])

            if initial_value < 0:
                withdraw_category += initial_value
        
        withdraw_per_category.append( withdraw_category)
       # GOOD: print(f'in {category.name}, you spent: {withdraw_category}')
    
    total_withdrew = sum(withdraw_per_category)
    # GOOD: print(f'in total, you spent: {total_withdrew}')

    data_percent_spent = []

    for withdraw_category in withdraw_per_category:
        spent_on_category = int(round((withdraw_category*100) / total_withdrew, -1))

        data_percent_spent.append(spent_on_category)
        

    # GOOD: print('Percent spent per category',data_percent_spent)


    # withdraw percent:
    percent_point = 'o'


    # percentage part:
    for percent in range(100, -1, -10):
        points = ''
        for percent_category in data_percent_spent:

            if percent_category >= percent:
                points += f'{percent_point}{2*space}'
            else:
                points += 3*space
                
        line_data = f"{percent:>3}| {points}{2*space}"

        #line_lenght = 5 + 3*len(data_percent_spent)

        #full_line_data = line_data + space*(line_lenght - len(line_data))
        #print(len(full_line_data))

        full_chart += line_data
        full_chart += '\n'
    


    # second part
    # seperating line
    bar = '---'
    seperating_line = f'{4*space}{bar*(len(data_percent_spent))}-\n'

    full_chart += seperating_line

    # The categories displayed horizentally:

    # finding the longest name:
    longest_name = 0
    for category in categories:
        if len(category.name) > longest_name:
            longest_name = len(category.name)
        else:
            continue

    for i in range(longest_name):

        # starting indention
        text_line = 5*space

        for category in categories:
            if i < len(category.name):
                text_line += f'{category.name[i]}{2*space}'
            else:
                text_line += f'{3*space}'
        
        text_line += '\n'

        full_chart += text_line
    
    full_chart = full_chart.strip()




   
    return full_chart


# testing area:


# Food Category
food = Category('Food')
food.deposit(1000, 'initial deposit')
food.withdraw(620, 'groceries')


#Clothing category
clothing = Category('Clothing')
food.transfer(1, clothing)


clothing.deposit(1000, 'initial deposit')

clothing.withdraw(204, 'restaurant and more food for dessert')

#Auto category
auto = Category('Auto')
clothing.transfer(1, auto)

auto.deposit(100000, 'initial deposit')
auto.withdraw(124, 'groceries')


auto.transfer(1, food)



stuff_list = [food, clothing, auto]

print(create_spend_chart(stuff_list))

Here is my output:

Your browser information:

User Agent is: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:151.0) Gecko/20100101 Firefox/151.0

Challenge Information:

Build a Budget App - Build a Budget App

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

GitHub Link: freeCodeCamp/curriculum/challenges/english/blocks/lab-budget-app/5e44413e903586ffb414c94e.md at main · freeCodeCamp/freeCodeCamp · GitHub

Welcome to the forum @abdoulfadylouattara,

Look up “python round down to the nearest 10”.

If you’re on a Windows machine, use the “Fn” key and “F12”. You can also use the CTRL-Shift-I shortcut to go right in. Make sure you’re on the “Console” tab. Then look for assertion errors:

'Perc[35 chars]       \n 90|            \n 80|            \n [359 chars]   t' !=
'Perc[35 chars]     \n 90|          \n 80|          \n 70|   [339 chars] t  '
'    [101 chars]       n  \n        m  \n        e  \n        n  \n        t' != 
'    [101 chars]       n  \n        m  \n        e  \n        n  \n        t  '

13 != 11 : Expected different length of the chart line. Check that all spacing is exact.

False is not true : Expected different rounding of bars.

Happy coding

So I tried with F12, modified a few things here and there. I still don’t fully get it, but the assertion error has changed to:

AssertionError: 'Perc[170 chars] 10| o  o  o  \n  0| o  o  o  \n    ----------[202 chars]   t' != 'Perc[170 chars] 10|    o  o  \n  0| o  o  o  \n    ----------[204 chars] t  '

It seems to me that the top line is linked to my code and the bottom is the correct answer. Anyway, I modified the spacing of the chart and found the best result to lead to this.

Now for the rounding part, I started with looking up how to do it and I’m still finding the same methods, originally I did it like this:

    for withdraw_category in withdraw_per_category:
        spent_on_category = int(round((withdraw_category*100) / total_withdrew, -1))

        data_percent_spent.append(spent_on_category)

I basically made a list of the percentages of amount spents. The other rounding methods seemed to weird to make sense

What other “rounding” methods did you try?

what I mean is that what I found online as other rounding methods were not clear, so I didn’t even try them.

Why not? What did you find when you googled this:

I mainly found using round as I did

The other thing I found was this on reddit:

 x = floor(x)/10; x = x*10

So far, I haven’t encountered floor(), so I skipped it

floor() is part of the math module, so you will need to import it if you use it.

I’m surprised you didn’t get more from your search.

I mean, round was the most common method and it seemed to work, so I just went ahead with it (I’m also quite lazy on making research)

It’s not exactly working for you though, is it? I’d continue to research.

Honestly (maybe stupid question), if you were to spend a week stuck on a project like this, wouldn’t you resort to AI? I have forced myself not to so far, but at this point I’m considering it big time

Maybe try again to do it on your own.

Without using code, what would you need to do to round the value $54.87 down to the nearest ten.

(edited)

What do you think the nearest ten should be?

this may have been the main cause of you getting stuck, instead of skipping something, you should spend some time learning about it

you are asked to round down a number

that means, 39 to 30, 38 to 30

classic rounding does round to the nearest number, which can be rounding up, or rounding down, 39 to 40, 32 to 30.

If you round, the tests will not accept your graphs, you must round down.

what do you think you would do if you go to the AI and the AI suggests floor that you skipped?

I swear, it is only now I realized I have to round DOWN… :man_facepalming:

Alright, now step 19 is validated

Ok, so now, for step 20 ( Each line in create_spend_chart chart should have the same length…), when I print the length of the line, I get 16 for all of them, yet it doesn’t pass

what is your latest code?

Its basically the same except for the bar height part. I’m wondering, when they ask for the same length for each line, do they include the title line? Because I think I tried it and didn’t work

from math import floor
class Category: 
        
    def __init__(self, name):
        self.name = name
        self.ledger = []
        self.balance = 0

    # the string value of the object
    def __str__(self):
        # title part
        full_display = ''

        empt = ' '
        dot = '*'
        dot_1 =  round((30 - len(self.name)) / 2)
        dot_2 = 30 - len(self.name) - dot_1
        title = f'{dot_1 * dot}{self.name}{dot_2 * dot}\n'

        full_display += title
        

        # list part
        # first, we reach each dict
        for transaction in self.ledger:

            descrip = transaction['description']

        # I don't believe it was really needed, but it works
            initial_value = (transaction['amount'])

            value = f"{initial_value: .2f}" 
            # as long as it works...

            if len(descrip) > 23:
                descrip = descrip[:23]
                space = 30 - len(descrip) -len(str(value))

                full_display += (f'{descrip}{space*empt}{value}\n')
            else:
                space = 30 - len(descrip) -len(str(value))

                full_display += (f'{descrip}{space*empt}{value}\n')

        # out of for loop
        full_display += f'Total: {self.balance}'
        
        # It will return the full list of transactions, transfers and current balance
        return full_display

    def deposit(self, amount, description = ''):

        self.balance += amount
        self.ledger.append({
            'amount': amount,
            'description': description
        })

    
    def withdraw(self, amount, description = ''):
        if self.check_funds(amount):
            self.balance -= amount 

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


    def get_balance(self):
        return self.balance

    def transfer(self, amount, destination):

        if self.check_funds(amount):
            self.withdraw(amount, f'Transfer to {destination.name}')

            destination.deposit(amount, f'Transfer from {self.name}')
            
            return True

        else:
            return False
    

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

# end of category class


def create_spend_chart(categories):
    full_chart = ''
    chart_title = 'Percentage spent by category\n'
    space =' '

    full_chart += chart_title

    withdraw_per_category = []
    for category in categories:
        withdraw_category = 0 # it reset for each category

        for transaction in category.ledger:
            initial_value = (transaction['amount'])

            if initial_value < 0:
                withdraw_category += initial_value
        
        withdraw_per_category.append( withdraw_category)
       # GOOD: print(f'in {category.name}, you spent: {withdraw_category}')
    
    total_withdrew = sum(withdraw_per_category)
    # GOOD: print(f'in total, you spent: {total_withdrew}')

    data_percent_spent = []

    for withdraw_category in withdraw_per_category:
        
        spent_on_category = 10*(floor((withdraw_category*10) / total_withdrew))


        data_percent_spent.append(spent_on_category)
        
    print('Percent spent per category',data_percent_spent)
    # GOOD: print('Percent spent per category',data_percent_spent)


    # withdraw percent:
    percent_point = 'o'


    # percentage part:
    for percent in range(100, -1, -10):
        points = ''
        for percent_category in data_percent_spent:

            if percent_category >= percent:
                points += f'{percent_point}{2*space}'
            else:
                points += 3*space
                
        line_data = f"{percent:>3}| {points}{2*space}"


        #full_line_data = line_data + space*(line_lenght - len(line_data))
        #print(len(full_line_data))

        full_chart += line_data
        full_chart += '\n'
    


    # second part
    # seperating line
    bar = '---'
    seperating_line = f'{4*space}{bar*(len(data_percent_spent))}-\n'

    full_chart += seperating_line

    # The categories displayed horizentally:

    # finding the longest name:
    longest_name = 0
    for category in categories:
        if len(category.name) > longest_name:
            longest_name = len(category.name)
        else:
            continue

    for i in range(longest_name):

        # starting indention
        text_line = 5*space

        for category in categories:
            if i < len(category.name):
                text_line += f'{category.name[i]}{2*space}'
            else:
                text_line += f'{3*space}'
        
        text_line += '\n'

        full_chart += text_line
    
    full_chart = full_chart.strip()




   
    return full_chart


# testing area:


# Food Category
food = Category('Food')
food.deposit(1000, 'initial deposit')
food.withdraw(620, 'groceries')


#Clothing category
clothing = Category('Clothing')
food.transfer(1, clothing)


clothing.deposit(1000, 'initial deposit')

clothing.withdraw(204, 'restaurant and more food for dessert')

#Auto category
auto = Category('Auto')
clothing.transfer(1, auto)

auto.deposit(100000, 'initial deposit')
auto.withdraw(124, 'groceries')


auto.transfer(1, food)



stuff_list = [food, clothing, auto]

print(create_spend_chart(stuff_list))

you can use the assertion error you can see in the browser console as a guide:

AssertionError: 'Perc[35 chars]       \n 90|            \n 80|            \n [359 chars]   t'
             != 'Perc[35 chars]     \n 90|          \n 80|          \n 70|   [339 chars] t  '

the firt line is your code, the second is the expected

you have some extra spaces at the end of each line

So I tried to use index with the strings of the lines, from 0 to 14 , and now I get this. It seems I have the correct amount of characters now, but it seems I’m still missing something

AssertionError: 'Perc[362 chars]           m  \n           e  \n           n  \n           t' != 'Perc[362 chars]           m  \n           e  \n           n  \n           t  '

Put the expected code below your code so you can easily see the difference:

'Perc[362 chars]           m  \n           e  \n           n  \n           t' != 
'Perc[362 chars]           m  \n           e  \n           n  \n           t  '