Budget App chart output seems correct but there is an error

This is my code but there is an error.
create_spend_chart should print a different chart representation. Check that all spacing is exact.
How to fix it?

class Category:
    def __init__(self, category):
        self.category = category
        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):
        return sum(item["amount"] for item in self.ledger)

    def transfer(self, amount, budget_category):
        if self.check_funds(amount):
            self.withdraw(amount, f"Transfer to {budget_category.category}")
            budget_category.deposit(amount, f"Transfer from {self.category}")
            return True
        return False

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

    def __str__(self):
        title = f"{self.category:*^30}\n"
        items = ""
        for item in self.ledger:
            items += f"{item['description'][:23]:23}{item['amount']:>7.2f}\n"
        total = f"Total: {self.get_balance():.2f}"
        return title + items + total


def create_spend_chart(categories):
    chart = "Percentage spent by category\n"

    # Calculate the percentage spent for each category
    spendings = [sum(item["amount"] for item in category.ledger if item["amount"] < 0) for category in categories]
    total_spent = sum(spendings)
    percentages = [int(spending / total_spent * 100) for spending in spendings]

    # Build the chart row by row
    for i in range(100, -1, -10):
        chart += str(i).rjust(3) + "| "
        for percentage in percentages:
            chart += "o" if percentage >= i else " "
            chart += "  "
        chart += "\n"

    # Add the bottom line
    chart += "    -" + "---" * len(categories) + "\n"

    # Find the maximum category name length
    max_name_length = max(len(category.category) for category in categories)

    # Build the category names row by row
    for i in range(max_name_length):
        chart += "     "
        for category in categories:
            if i < len(category.category):
                chart += category.category[i] + "  "
            else:
                chart += "   "
        chart += "\n"

    return chart.rstrip()

# Example usage
food = Category("Food")
food.deposit(1000, "deposit")
food.withdraw(10.15, "groceries")
food.withdraw(15.89, "restaurant and more food for dessert")

clothing = Category("Clothing")
food.transfer(250, clothing)
clothing.deposit(1000, "deposit")
clothing.transfer(100, food)

print(create_spend_chart([food, clothing]))

https://www.freecodecamp.org/learn/scientific-computing-with-python/scientific-computing-with-python-projects/budget-app

Have a look at this thread:
https://forum.freecodecamp.org/t/scientific-computing-with-python-projects-budget-app/677914

Here is the expected output that you need to match:

Percentage spent by category\n100|          \n 90|          \n 80|          \n 70|    o     \n 60|    o     \n 50|    o     \n 40|    o     \n 30|    o     \n 20|    o  o  \n 10|    o  o  \n  0| o  o  o  \n    ----------\n     B  F  E  \n     u  o  n  \n     s  o  t  \n     i  d  e  \n     n     r  \n     e     t  \n     s     a  \n     s     i  \n           n  \n           m  \n           e  \n           n  \n           t

And here is the test data:

food = Category("Food")
entertainment = Category("Entertainment")
business = Category("Business")    
food.deposit(900, "deposit")
entertainment.deposit(900, "deposit")
business.deposit(900, "deposit")

food.withdraw(105.55)
entertainment.withdraw(33.40)
business.withdraw(10.99)

print(repr(create_spend_chart([business, food, entertainment]))

You can use repr() to print at the end to show raw string output

You can also read through this thread:
https://forum.freecodecamp.org/t/scientific-computing-with-python-projects-budget-app/677107

1 Like
Percentage spent by category\n100|          \n 90|          \n 80|          \n 70|    o     \n 60|    o     \n 50|    o     \n 40|    o     \n 30|    o     \n 20|    o  o  \n 10|    o  o  \n  0| o  o  o  \n    ----------\n     B  F  E  \n     u  o  n  \n     s  o  t  \n     i  d  e  \n     n     r  \n     e     t  \n     s     a  \n     s     i  \n           n  \n           m  \n           e  \n           n  \n           t

Results are same but I canā€™t pass this test.

Iā€™ve edited your code for readability. When you enter a code block into a forum post, please precede it with a separate line of three backticks and follow it with a separate line of three backticks to make it easier to read.

You can also use the ā€œpreformatted textā€ tool in the editor (</>) to add backticks around text.

See this post to find the backtick on your keyboard.
Note: Backticks (`) are not single quotes (').

1 Like

This is my codeā€¦

class Category:
    def __init__(self, category):
        self.category = category
        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):
        return sum(item["amount"] for item in self.ledger)

    def transfer(self, amount, budget_category):
        if self.check_funds(amount):
            self.withdraw(amount, f"Transfer to {budget_category.category}")
            budget_category.deposit(amount, f"Transfer from {self.category}")
            return True
        return False

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

    def __str__(self):
        title = f"{self.category:*^30}\n"
        items = ""
        for item in self.ledger:
            items += f"{item['description'][:23]:23}{item['amount']:>7.2f}\n"
        total = f"Total: {self.get_balance():.2f}"
        return title + items + total


def create_spend_chart(categories):
    chart = "Percentage spent by category\n"

    # Calculate the percentage spent for each category
    spendings = [sum(item["amount"] for item in category.ledger if item["amount"] < 0) for category in categories]
    total_spent = sum(spendings)
    percentages = [int(spending / total_spent * 100) for spending in spendings]

    # Build the chart row by row
    for i in range(100, -1, -10):
        chart += str(i).rjust(3) + "| "
        for percentage in percentages:
            chart += "o  " if percentage >= i else "   "
        chart += "\n"

    # Add the bottom line
    chart += "    -" + "---" * len(categories) + "\n"

    # Find the maximum category name length
    max_name_length = max(len(category.category) for category in categories)

    # Build the category names row by row
    for i in range(max_name_length):
        chart += "    "
        for category in categories:
            if i < len(category.category):
                chart += " " + category.category[i] + " "
            else:
                chart += "   "
        chart += "\n"

    return chart.rstrip()

# Example usage
food = Category("Food")
entertainment = Category("Entertainment")
business = Category("Business")    
food.deposit(900, "deposit")
entertainment.deposit(900, "deposit")
business.deposit(900, "deposit")

food.withdraw(105.55)
entertainment.withdraw(33.40)
business.withdraw(10.99)

print(create_spend_chart([business, food, entertainment]))```

Confirmed, it does match exactly.

Not sure what happened but it looks like there now needs to be two extra spaces at the end. Here is the expected output (I added single quotes so you can see the two spaces at the end):

'Percentage spent by category\n100|          \n 90|          \n 80|          \n 70|    o     \n 60|    o     \n 50|    o     \n 40|    o     \n 30|    o     \n 20|    o  o  \n 10|    o  o  \n  0| o  o  o  \n    ----------\n     B  F  E  \n     u  o  n  \n     s  o  t  \n     i  d  e  \n     n     r  \n     e     t  \n     s     a  \n     s     i  \n           n  \n           m  \n           e  \n           n  \n           t  '

Maybe it changed or maybe I didnā€™t copy it correctly in the first placeā€¦

1 Like

image

ā€˜Percentage spent by category\n100| \n 90| \n 80| \n 70| o \n 60| o \n 50| o \n 40| o \n 30| o \n 20| o o \n 10| o o \n 0| o o o \n ----------\n B F E \n u o n \n s o t \n i d e \n n r \n e t \n s a \n s i \n n \n m \n e \n n \n tā€™

I think it match exactly, :slight_smile:

You need 2 more spaces after the final t

1 Like

Make sense, I will try again.

1 Like

I added two spaces after final t, but still canā€™t pass this. :sleepy:

The rstrip in the return will remove them again.

Youā€™re almost there!

You are right, if I use rstrip, it remove them again.
Thatā€™s why and how can I pass it?

You could add them after the rstripā€¦ itā€™s not an ideal solution since that wonā€™t really work for every case butā€¦

The idea is that the spaces should form a grid like this:
Screenshot 2024-03-10 211440

You can see this is missing two spaces after the final 't' to form that grid:

Screenshot 2024-03-10 211723

1 Like

Understand.
I am trying to fix it, but not doing wellā€¦ :sleepy:

image

I replace chart += "\n" to chart += " \n".
And also I used ā€˜*ā€™ instead space, after then I can see correct result.
But I canā€™t still pass this test.

This looks good, but when you go back to spaces the rstrip will remove the last spaces again.

(Will also need to watch out for a trailing \n)

If rstrip() is not used, it returns a space and ā€˜\nā€™.
But still error ā€¦ :frowning:

You can add some logic that says

If this is not the last line,
    add `\n`
but if this is the last line, 
   don't add `\n`, add a space, or whatever you need

You could also be calculating how many characters wide each line will be and dynamically add spaces that way

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