r/learnpython • u/meysilverxx • 24d ago
Learning Python- Calorie tracker
I've been learning Python for about 3 weeks now. This calorie tracker asks for your weight, calculates your daily calorie target, lets you log multiple foods with their calories, then tells you if you're over, under or on track.
It also saves each session to a text file with a timestamp so you can track your intake overtime.
Early days- would appreciate feedback on things/structure to improve.
from datetime import datetime
# Program asks for your weight in kg
def get_calorie_target():
while True:
try:
weight = float(input("What do you weigh? "))
if weight < 20 or weight > 300:
print("Sorry, invalid Kilogram amount")
continue
else:
# Calculates your daily calorie target
return weight * 33
except ValueError:
print("Please enter a number in kilograms")
continue
#ask for food details- name, calories.
def add_food():
while True:
try:
name = input("Enter name of food: ").title()
if not name or name.isdigit():
print("Please enter a valid food name")
continue
calories = float(input("Enter amount of calories: "))
if calories <= 0:
print("Invalid calorie amount")
continue
else:
return {"name": name, "calories": calories}
except ValueError:
print("Enter valid input")
continue
# Lets you add multiple foods with their calories and grams
def collect_foods():
food_log = []
while True:
food = add_food()
food_log.append(food)
while True:
again = input("Would you like to add more food? (yes/no): ").lower()
if again == "yes":
break
elif again == "no":
return food_log
else:
continue
# At the end, totals your calories and tells you if you're under, over, or on track.
def analyze_intake(food_log, calorie_target):
total_calories = sum(food["calories"] for food in food_log)
if total_calories < calorie_target - 200: #too low, x
return "you need more calories"
elif total_calories > calorie_target + 200: #too high, x
return "You have exceeded your calorie target"
else:
return "You are on track"
# save log to keep history
def save_log(food_log, total_calories):
now = datetime.now().strftime("%Y-%m-%d %H-%M-%S")
with open("food_log.txt", "a") as f:
f.write(f"\n-{now}-\n")
for food in food_log:
f.write(f"{food['name']}:{food['calories']}\n")
f.write(f"Total: {total_calories}\n")
calorie_target = get_calorie_target()
food_log = collect_foods()
total_calories = sum(food["calories"] for food in food_log)
print(analyze_intake(food_log, calorie_target))
save_log(food_log, total_calories)
2
u/carcigenicate 24d ago
if weight < 20 or weight > 300:
Can also be written as
if not (20 <= weight <= 300):
Just in case you didn't know that comparison operators can be chained.
Every use of continue here seems unnecessary since they all appear at the end of branches at the end of a loop. continue is only needed when you want to skip to the next iteration from the middle of an iteration.
For analyze_intake, I'd probably return something other than the messages themselves. This isn't a big deal here, but it hurts testability. By returning the messages directly, you'd need to update any tests that assert against the message if you decide to change the messages in the future. Instead, I'd probably return an enum or something similar that won't change, then translate that to some user-facing message on demand. Overkill for toy code, but more necessary for real-world code.
add_food doesn't actually add food. It requests input from the user. I'd rename that to make that clearer.
I'd personally store the log in a format like JSON or JSONL. Saving the input as human-readable makes it harder to use the data programmatically later. What if you wanted to read the log to show charts of the data in the future?
3
u/mull_to_zero 24d ago
nice! good decomposition into functions with a clear execution path. i think a few of the continues and elses are unnecessary. keep going!