Calories Tracker Project — Round 5/7
Third report. The most complete one — and the most useful to share.
The first two reports are for us. Quick checks. Daily discipline. The general report is for someone else — a doctor, a nutritionist, a trainer. Everything logged, organized by day, with the full menu and the hour each item was consumed. A document worth presenting.
We start the same way.
# food_dict = {}
# def add_food_item(): [...]
# def last_days_details(): [...]
# def print_details(): [...]
# def last_days_sum(): [...]
# def print_target(): [...]
def general_report():
pass
Before we write a single line of logic, let's think about the structure we want to build. Our current food_dict looks like this:
{"2025-04-02,14:32:11": ["Chicken Breast", 200.0, "g", 175]}
The key is a full timestamp. For the general report, we want to group by day — with each day holding a calorie total and a list of everything consumed, indexed by time. The target structure is:
{"2025-04-02": [602, {"14:32:11": ["Chicken Breast", 200.0, "g", 175]}]}
Index 0 is the daily calorie total. Index 1 is a dictionary of entries, keyed by time. This is the most complex structure we've built so far — a list that contains both an integer and a dictionary, all nested inside another dictionary. Take a moment with it before moving on. The logic that follows is easier once the shape is clear.
Note: in this example, 602 represents the full daily total — the sum of all meals consumed that day. 175 is the calorie value for Chicken Breast alone, one entry among several. The structure shown here captures only one product for readability.
We build it in steps. First — the skeleton, without the calorie sum:
def general_report():
rep_dict = {}
for key in food_dict:
if key.split(",")[0] not in rep_dict:
rep_dict[key.split(",")[0]] = [0, {key.split(",")[1]: [food_dict[key][0], food_dict[key][1], food_dict[key][2], food_dict[key][3]]}]
else:
rep_dict[key.split(",")[0]][1][key.split(",")[1]] = [food_dict[key][0], food_dict[key][1], food_dict[key][2], food_dict[key][3]]
return rep_dict
When we see a date for the first time — if key.split(",")[0] not in rep_dict — we create the entry: a list with 0 as placeholder at index 0, and the first product in a new inner dictionary at index 1. The inner dictionary key is the time part: key.split(",")[1].
When the date already exists — else — we add the new product to the inner dictionary. Note the path: rep_dict[key.split(",")[0]][1][key.split(",")[1]]. We go into the day's entry, then into index 1 (the inner dict), then add the new time key.
Now we add the calorie sum. The placeholder 0 at index 0 becomes the first product's calories on creation, and accumulates from there:
def general_report():
rep_dict = {}
for key in food_dict:
if key.split(",")[0] not in rep_dict:
rep_dict[key.split(",")[0]] = [food_dict[key][3], {key.split(",")[1]: [food_dict[key][0], food_dict[key][1], food_dict[key][2], food_dict[key][3]]}]
else:
rep_dict[key.split(",")[0]][0] += food_dict[key][3]
rep_dict[key.split(",")[0]][1][key.split(",")[1]] = [food_dict[key][0], food_dict[key][1], food_dict[key][2], food_dict[key][3]]
return rep_dict
On first entry: index 0 is set to food_dict[key][3] — the calories of the first product. On every subsequent entry for the same day: rep_dict[key.split(",")[0]][0] += food_dict[key][3] adds to it. Clean accumulation.
Now we add date filtering. We want the user to define a period — start and stop. Same pattern we've used before for string inputs:
def general_report():
rep_dict = {}
start_string = ""
while not start_string:
start_string = input("Start date (YYYY-MM-DD, e.g. 2025-04-01): ")
stop_string = ""
while not stop_string:
stop_string = input("Stop date (YYYY-MM-DD, e.g. 2025-04-07): ")
for key in food_dict:
if start_string <= key.split(",")[0] <= stop_string:
if key.split(",")[0] not in rep_dict:
rep_dict[key.split(",")[0]] = [food_dict[key][3], {key.split(",")[1]: [food_dict[key][0], food_dict[key][1], food_dict[key][2], food_dict[key][3]]}]
else:
rep_dict[key.split(",")[0]][0] += food_dict[key][3]
rep_dict[key.split(",")[0]][1][key.split(",")[1]] = [food_dict[key][0], food_dict[key][1], food_dict[key][2], food_dict[key][3]]
return rep_dict
The filter condition: start_string <= key.split(",")[0] <= stop_string. This works because our dates are in YYYY-MM-DD format — and for this format, alphabetical comparison and chronological comparison are identical. Python compares strings character by character, left to right. "2025-04-01" is less than "2025-04-07" for the same reason "a" is less than "g". We get correct date ordering for free.
The report is built. Now we display it:
def print_general():
food_log = general_report()
for key in sorted(food_log):
print("=" * 20)
print(f"Date: {key} - {food_log[key][0]} kcal consumed.")
print("=" * 20)
day_dict = food_log[key][1]
for subkey in sorted(day_dict):
print(f"Time: {subkey} - {day_dict[subkey][0]}, {day_dict[subkey][1]}{day_dict[subkey][2]} - {day_dict[subkey][3]} kcal.")
We extract food_log[key][1] into day_dict before the inner loop. It's a small thing — but it makes the code easier to read and easier to debug. Instead of food_log[key][1][subkey][0], we write day_dict[subkey][0]. Same result. Less noise.
Both sorted(food_log) and sorted(day_dict) give us chronological order — days from oldest to newest, and within each day, entries from earliest to latest. Same reason as before: YYYY-MM-DD and HH:MM:SS both sort correctly as strings.
The output for a two-day period might look like this:
====================
Date: 2025-04-01 - 602 kcal consumed.
====================
Time: 08:14:22 - Oats, 80.0g - 297 kcal.
Time: 12:45:03 - Chicken Breast, 200.0g - 175 kcal.
Time: 19:30:11 - White Rice, 100.0g - 130 kcal.
====================
Date: 2025-04-02 - 527 kcal consumed.
====================
Time: 07:55:44 - Greek Yogurt, 150.0g - 88 kcal.
Time: 13:10:09 - Salmon, 180.0g - 350 kcal.
Time: 20:05:33 - Banana, 1.0pcs - 89 kcal.
Into the menu:
while True:
option = input("Choose option (1-Add, 2-Show food details, 3-Discipline analysis, 4-General report, q-Quit): ")
if option == "1":
add_food_item()
print("Food log added successfully!")
elif option == "2":
if len(last_days_details()):
print_details()
else:
print("No data available.")
elif option == "3":
if len(last_days_sum()):
print_target()
else:
print("No data available.")
elif option == "4":
if len(general_report()):
print_general()
else:
print("No data available.")
elif option == "q":
break
else:
print("Invalid option!")
Four options. Three reports. One input function. Everything connected.
We know what we want now. The data flows in, gets organized, gets filtered, gets displayed — all on our terms. What's missing is permanence. Right now, everything lives in memory. Close the program and it's gone.
That changes next. We add persistence — and when we do, the general report stops being a print function and becomes a file. Something you can copy to a USB drive, attach to an email, hand to a doctor.
The camp is established. Now we build shelter.