Build a Budget App: Chart

I’m stuck on the graph for the budget app in Python. My function that makes the circles for the graph is:

def _make_circles(percent):
    circle_number = percent * 10
    circle = ""
    i = 10
    while i >= 0:
       if i > circle_number:
            circle = circle + " "
        else:
            circle = circle + "o"
        i -= 1
    return circle

So, if I pass it _make_circles(0.19) I get oo which when charted in the graph looks like:

10| o     
  0| o  
    ----
     F   
     o  
     o  
     d  

But, if I pass _make_circles(0.09) or _make_circles(0.099) , I get o and the graph looks like:

 20| 
 10|    
  0| o  
    ----
     F  
     o  
     o  
     d  

The instructions say that it should round down to the nearest 10. It appears to be rounding down correctly, however, in the test, I’m getting an error because I have one extra o in one column for 10% mark (the Business category). I can’t figure out where the extra o is coming from and I can’t reproduce it. I also don’t know what values the test is passing into the category, so I can’t reproduce it in the terminal. I appreciate any hints that may push me in the correct direction.

These are values used in the last test. To use them for manual testing remove all self. and budget., they are relevant for testing tooling:

        self.food = budget.Category("Food")
        self.entertainment = budget.Category("Entertainment")
        self.business = budget.Category("Business")

        self.food.deposit(900, "deposit")
        self.entertainment.deposit(900, "deposit")
        self.business.deposit(900, "deposit")
        self.food.withdraw(105.55)
        self.entertainment.withdraw(33.40)
        self.business.withdraw(10.99)
        actual = budget.create_spend_chart([self.business, self.food, self.entertainment])

To give you more concrete hint, seeing the rest of the code would be needed.

Perfect. I’ll try to trouble shoot with these and come back and post more code if I still can’t get it to work. Thank you.

Update:
I got the upper part working. I’m still getting one error on the last line:

 -            t  
 ?               -
 +            t   : Expected different chart representation. Check that all spacing is exact.

What is it looking for/not looking for here?

look at the Assertion error, you should see it there

Thank you. It’s now passed all the tests. I think it didn’t like that there was a return after my last element. It’s these little bugs at the end that get you. I have no idea if my code is actually good, but it works.

code that works is the first step, as second step you can consider readability: clear variable names, comments to explain things etc

If you have time, I’d appreciate any feedback you may have about how I solved this. I feel like my solutions tend to be a bit hacky. For example, there may be methods that I’m forgetting about that could condense a bit of this. But it does work, so there’s that.

#Creates the budget category class and class methods
class Category:
    def __init__(self, category):
        self.category = category
        self.amount = 0
        self.description = ""
        self.ledger = []

    #When printing the category, prints the list of category transactions.
    def __str__(self):
        return f'{self.print_category(self.category)}'

    #Adds ledger entry for deposit with amount and optional description
    def deposit(self, amount, description = ""):
        self.ledger.append({'amount': amount, 'description': description})
    #Adds ledger entry for withdrawal with amount and optional description
    def withdraw(self, amount, description = ""):
        if self.check_funds(amount):
            amount = amount * -1
            self.ledger.append({'amount': amount, 'description': description})
            return True
        else:
            return False     
    #Returns current balance of category.
    def get_balance(self):
        bal = 0
        i = 0
        for item in self.ledger:
            amount = self.ledger[i]["amount"]
            bal = bal + amount
            i += 1
        return bal
    #Adds ledger entry for each category with deposit/withdrawal amount and description of where the transfer came from/to
    def transfer(self, amount, other_category):
        if self.check_funds(amount):
            description = 'Transfer to ' + other_category.category
            self.withdraw(amount, description)
            description = 'Transfer from ' + self.category
            other_category.deposit(amount, description)
            return True
        else: return False
    # Accepts an amount as an argument to see if there are sufficient funds for a transaction
    def check_funds(self, amount):
        if amount > self.get_balance():
            return False
        else:
            return True
    #creates a list of the transactions in the category beneath the category name with asterisks filling up to 30 total characters and the category's current balance
    def print_category(self, name):
        stars = (30 - len(self.category)) // 2
        star_line = ""
        title = ""
        i = 0
        while i < stars:
            star_line = star_line + "*"
            i += 1
        if len(f'{star_line}{name}{star_line}') != 30:
            title = f'{star_line}{name}{star_line}*'

        else:
            title = f'{star_line}{name}{star_line}'
        ret_string = ""
        for index in range(len(self.ledger)):
            size = len(f'{self.ledger[index]["description"][:23]}{self.ledger[index]["amount"]:.2f}')
            spaces = ""
            x = 0
            while x < 30 - size:
                spaces = spaces + " "
                x += 1
            ret_string = ret_string + f'{self.ledger[index]["description"][:23]}{spaces}{self.ledger[index]["amount"]:.2f}\n'   
        total = f'Total: {self.get_balance():.2f}'
        return f'{title}\n{ret_string}{total}'

#Creates a chart to show the distribution of spending as a percentage of total spent
def create_spend_chart(categories):
    total = _get_total(categories)
    cat_totals = []
    sumline= _make_sum_line(categories)
    i = 0
    #creates a tuple of the category name and a string of os with each o representing 10% of total spending rounded down to the nearest 10%.
    while i < len(categories):
        cat_totals.append((categories[i].category, _make_circles(round(_get_cat_total(categories[i])/total,2))))
        i += 1
    names = _make_category_names(categories)
    sa = []
    ss = " "
    j = 0
    #iterates through each category string of " " and "o" stored in cat_totals to place create an array (sa) of strings (ss). Each item of the category strings are separated by 2 spaces. These are then used to return the graph line by line in the return statement, so sa[0] would return the first ss string containing either " " or "o" for the category followed by two spaces between each.
    while j <= 10:
        for item in cat_totals:
            ss = ss + item[1][j] + "  "
        sa.append(ss)
        ss = " "
        j += 1
    return(f'Percentage spent by category\n100|{sa[0]}\n 90|{sa[1]}\n 80|{sa[2]}\n 70|{sa[3]}\n 60|{sa[4]}\n 50|{sa[5]}\n 40|{sa[6]}\n 30|{sa[7]}\n 20|{sa[8]}\n 10|{sa[9]}\n  0|{sa[10]}\n{sumline}\n{names}')

#Generates a sum of all the spending transactions in a category
def _get_cat_total(category):
    spending_sum = 0
    i = 0
    while i < len(category.ledger):
        if category.ledger[i]['amount'] < 0:
            spending_sum = spending_sum + round(category.ledger[i]['amount'],2)
            i += 1
        else:
            i +=1
    return round(spending_sum,2)

#Generates a sting of " " and "o". Each circle represents 10% or more in spending for a category against the total spending. All categories will have a "o" for 0%, to the string is 11 characters long.
def _make_circles(percent):
    circle_number = percent * 10
    circle = ""
    i = 10
    while i >= 0:
        if i > circle_number:
            circle = circle + " "
        else:
            circle = circle + "o"
        i -= 1
    return circle

#Generates a sum of all spending in all categories to be used to determin percentage of spending in each category
def _get_total(categories):
    total = 0
    i = 0
    while i < len(categories):
        total = total + _get_cat_total(categories[i])
        i += 1
    return round(total,2)

#Generates the X-axis for the spending chart that extends two spaces past the final bar
def _make_sum_line(categories):
    sumline = "    -"
    i = 1
    while i <= len(categories):
        sumline = sumline + "---"
        i += 1
    sumline = sumline + ""
    return sumline

#Similar to the impletmentation of the circles above. Iterates over each name and adds the letter at the current index to a string. The string will print the category names vertically with two spaces between each letter. The string does not have an enter at the end.
def _make_category_names(categories):
    name_list = []
    for name in categories:
        name_list.append(name.category)
    max_length = (len(max(name_list, key = len)))
    test = "     "
    out = []
    i = 0
    while i < max_length:
        for name in name_list:
            if i >= len(name):
                test = test + "   "
            else:
                test = test + name[i] + "  "
        out.append(test)
        i += 1
        test = "     "
    return_string = ""
    for num, index in enumerate(out):
        if num == len(out)-1:
            return_string = return_string + index
        else:
            return_string = return_string + index + "\n"
    return return_string

if you want feedback, post in Code Feedback

Perfect. Thank you. I will post in the correct forum.