Build a Persistent Activity Tracker
Build a Python CLI that saves and reloads activity entries from a text file.
Introduction
30 Second Summary
Small activities can fill a day without leaving a clear record of where the time went. Closing a simple program can also erase the history you just entered.
In this project, you will build a terminal-based activity tracker in Python. You'll see entries disappear after a restart before using a local text file to bring them back.
What You'll Build
You'll demo a tracker that restores your complete activity history after a restart.
By the end of this project, you'll have:
- An interactive menu that records several activities during one session.
- A numbered activity log that shows each duration plus the total time across all entries.
- A persistent activity history that returns when you close the tracker before running it again.
- Secret Mission: Add input validation that blocks blank or pipe-containing names plus non-positive or non-numeric durations.
Are there any prerequisites?
You'll need a Windows 10 or newer device. Familiarity with basic programming ideas plus basic command-line use is helpful.
Step 1 checks your Windows version before setting up Python 3.15.0 with IDLE, so you do not need previous Python experience.
Before We Start
This step gives you a clear goal before any hands-on work begins. Your goal is a local command-line activity tracker that records each activity with its duration.
Set Up Python and IDLE
Your activity tracker needs a local runtime that can execute each line you write. A version mismatch could stop the first script before the tracker takes shape.
You'll verify your Windows version first. Then you'll confirm Python 3.15.0 through IDLE 3.15.0 by running a visible starter script.
In this step, get ready to:
- Verify that your device uses Windows 10 or Windows 11.
- Prepare Python 3.15.0 with its included IDLE editor.
- Run activity_tracker.py in IDLE to produce your first output.
Verify your Windows version
Python 3.15.0 supports Windows 10 and newer. Checking your version confirms that your device can run the project tools.
- Press the Windows key to open Search.
- Type winver into Search.
- Press Enter to open the version dialog.
- Read the Windows edition shown in the dialog.
You should see Windows 10 or Windows 11. This confirms that your device supports Python 3.15.0.
Seeing an Earlier Windows Version?
This project requires Windows 10 or Windows 11. Update Windows before continuing with the Python installation.
Use Help me check whether my Windows device can run Python 3.15.0..
Confirm Python and open IDLE
Windows PowerShell lets you check which Python runtime responds to commands. The version check separates a ready installation from an older or missing one.
- Close the Windows version dialog.
- Press the Windows key to open Search.
- Type Windows PowerShell into Search.
- Press Enter to open Windows PowerShell.
- Check your installed Python version by running this command:
python --version
✔️ I see the required version
Your runtime is ready. PowerShell is using Python 3.15.0.
- Keep the current Windows PowerShell window open.
ⓧ I see an older version
PowerShell is finding an older Python installation. Installing Python 3.15.0 gives this project the expected runtime and IDLE version.
- Open the official Python Windows downloads page in your browser.
- Find Python 3.15.0 on the downloads page.
- Select Download Windows installer (64-bit).
- Open the downloaded installer from your browser's downloads panel.
- Approve the Windows permission request if it appears.
- Leave the default installer features enabled.
- Click Install Now.
Expect the installation to take a few minutes while Python and IDLE are copied to your device.
- Wait for the installer to report that installation completed successfully.
- Close the installer.
- Close the current Windows PowerShell window.
- Press the Windows key to open Search.
- Type Windows PowerShell into Search.
- Press Enter to reopen Windows PowerShell.
- Confirm the new installation by running the version check again:
python --version
You should now see Python 3.15.0 in the output.
ⓧ Command not found
Windows cannot find a Python runtime yet. The official Python 3.15.0 installer includes IDLE when you keep its default features enabled.
- Open the official Python Windows downloads page in your browser.
- Find Python 3.15.0 on the downloads page.
- Select Download Windows installer (64-bit).
- Open the downloaded installer from your browser's downloads panel.
- Approve the Windows permission request if it appears.
- Leave the default installer features enabled.
- Click Install Now.
Expect the installation to take a few minutes while Python and IDLE are copied to your device.
- Wait for the installer to report that installation completed successfully.
- Close the installer.
- Close the current Windows PowerShell window.
- Press the Windows key to open Search.
- Type Windows PowerShell into Search.
- Press Enter to reopen Windows PowerShell.
- Confirm the installation by running the version check again:
python --version
You should now see Python 3.15.0 in the output.
Every path now leads to the same verified runtime. Next, PowerShell can launch the IDLE editor included with that installation.
- Launch IDLE from Windows PowerShell by running this command:
python -m idlelib
You should see the IDLE Shell window open. This confirms that PowerShell can reach IDLE through Python.
- Click Help in the IDLE Shell menu.
- Select About IDLE.
- Confirm that the version information identifies IDLE 3.15.0.
- Close the version information window.
IDLE Not Opening?
Close Windows PowerShell after installing Python. Reopen it before trying the launch command again.
If IDLE still does not open, run the Python 3.15.0 installer again. Choose Install Now with the default features enabled so Tcl/Tk and IDLE are included.
Use Help me launch IDLE from Windows PowerShell..
Create and run the starter script
IDLE separates the editor from the Shell. You write saved Python code in an editor window and see its output in the Shell.
- Click File in the IDLE Shell menu.
- Select New File to create a blank editor window.
- Add the starter message to the new editor window by copying this code:
print("Activity Tracker ready")
What Does This Code Do?
The print() function sends text to the IDLE Shell. The message gives you a visible signal that the saved file ran successfully.
- Click File in the editor window.
- Select Save As….
- Select Desktop as the save location.
- Enter activity_tracker.py as the file name.
- Click Save.
Before you run the file, what text do you expect the Shell to display?
- Click Run in the editor window.
- Select Run Module.
You should see Activity Tracker ready in the IDLE Shell. That's your first working result: Windows has executed code from the saved activity_tracker.py file.
Don't See the Starter Message?
Confirm that you typed the line in the editor window. Code entered directly at the Shell prompt is not part of the saved file.
Check that the filename is exactly activity_tracker.py. Then compare the quotation marks and parentheses with the code above.
Use Help me fix my IDLE starter script..
✔️ Awesome, I've got everything!
Your starter file is saved. Double-check that the IDLE Shell displays Activity Tracker ready.
ⓧ I'd like to double check the full code
Compare your saved activity_tracker.py file with this complete version.
print("Activity Tracker ready")
Your Windows workspace now runs a saved Python program. Next, you'll replace the starter message with inputs that capture your first activity.
Capture Your First Activity
Your starter script proves that IDLE can run your Python file. The tracker still needs a real activity to work with.
One entry creates a small feedback loop before menus or files add more moving parts.
You will collect an activity name plus a duration. The script stores the pair before printing the saved entry in the Shell.
In this step, get ready to:
- Replace the starter message with input prompts.
- Store the activity as a tuple inside a list.
- Display the stored activity with an f-string.
Capture and store one activity
The input() function pauses the script while you type an answer. The int() function converts the duration into an integer.
A tuple keeps the activity name beside its duration. The entries list gives that tuple a place in the tracker.
- Switch back to activity_tracker.py in the IDLE editor from earlier.
- Select the existing print("Activity Tracker ready") line.
- Replace the selected line with the complete one-entry tracker by pasting this code:
activity = input("Activity: ")
minutes = int(input("Minutes: "))
entries = [(activity, minutes)]
saved_activity, saved_minutes = entries[0]
print(f"{saved_activity}: {saved_minutes} minutes")
What does this code do?
- The activity variable holds the text entered at the first prompt.
- The minutes variable holds the converted whole number from the second prompt.
- The entries list contains one tuple with both values.
- The entries[0] expression retrieves the first tuple from the list.
- Tuple unpacking assigns the two stored values to saved_activity and saved_minutes.
- The f-string inserts both saved values into the final output.
Run your first activity
Selecting Run Module executes the saved file from the beginning. This run tests the complete journey from both prompts to the printed entry.
- Save activity_tracker.py by pressing Ctrl+S.
Before you run the script, consider what line you expect after entering Reading and 25.
- Select Run Module from the IDLE editor menu.
- Enter Reading at the Activity: prompt.
- Enter 25 at the Minutes: prompt.
You should see Reading: 25 minutes in the IDLE Shell. Great. Your tracker now turns two answers into one displayed activity.
Prompts or activity missing?
- Confirm that activity_tracker.py is saved before selecting Run Module.
- Make sure the IDLE editor window has focus when you select Run Module.
- Enter a whole number such as 25 when the script asks for minutes.
- Help me troubleshoot my one-entry activity tracker.
✔️ Awesome, I've got everything!
Your saved activity_tracker.py now contains the complete one-entry tracker.
ⓧ I'd like to double check the full code
- Compare your saved activity_tracker.py with this complete version.
activity = input("Activity: ")
minutes = int(input("Minutes: "))
entries = [(activity, minutes)]
saved_activity, saved_minutes = entries[0]
print(f"{saved_activity}: {saved_minutes} minutes")
Your tracker now turns two answers into a displayed activity. Next, a menu loop lets you record more than one activity per run.
Build a Menu Loop
Your one-entry Python tracker captures useful data. It ends immediately after displaying that entry.
A menu powered by a while loop keeps the program ready for another choice. This step tests how far one in-memory list can carry the tracker.
In this step, get ready to:
- Repeat an interactive menu with a loop.
- Append several activity tuples to one entries list.
- Display numbered entries before testing a restart.
Build the repeating menu
The loop prints the same menu after each completed choice. Conditional branches decide whether to add data, display data, or stop the loop.
- Replace all existing code in activity_tracker.py in the IDLE editor from earlier with the code below:
entries = []
while True:
print("\nActivity Tracker")
print("1. Add activity")
print("2. View activities")
print("3. Quit")
choice = input("Choose an option: ").strip()
if choice == "1":
activity = input("Activity: ").strip()
minutes = int(input("Minutes: "))
entries.append((activity, minutes))
print("Activity added.")
elif choice == "2":
if not entries:
print("No activities yet.")
else:
print("\nActivity log")
for number, (activity, minutes) in enumerate(entries, start=1):
print(f"{number}. {activity}: {minutes} minutes")
elif choice == "3":
print("Goodbye!")
break
else:
print("Choose 1, 2, or 3.")
What does this code do?
- The empty entries list is created before the loop. Every menu pass uses that same list.
- The while True loop repeats until break runs.
- The choice variable stores the menu option entered in the Shell.
- The append() call adds each activity-minute tuple to the end of the list.
- The enumerate(entries, start=1) call provides a number for each saved tuple. Numbering begins at 1.
- The final else branch responds when the choice is outside the three menu options.
- Save activity_tracker.py.
- In IDLE, choose Run Module.
You'll see Activity Tracker above three choices. The Shell waits at Choose an option:.
Menu not appearing?
- Confirm that you saved activity_tracker.py before choosing Run Module.
- Compare the indentation inside while True with the reference code. Each nested block must remain indented.
- Check that every opening parenthesis or quotation mark has a matching closing character.
- Help me troubleshoot my menu loop.
✔️ Awesome, I've got everything!
Great, your menu loop is saved in activity_tracker.py.
ⓧ I'd like to double check the full code
Compare your saved file with this complete version.
entries = []
while True:
print("\nActivity Tracker")
print("1. Add activity")
print("2. View activities")
print("3. Quit")
choice = input("Choose an option: ").strip()
if choice == "1":
activity = input("Activity: ").strip()
minutes = int(input("Minutes: "))
entries.append((activity, minutes))
print("Activity added.")
elif choice == "2":
if not entries:
print("No activities yet.")
else:
print("\nActivity log")
for number, (activity, minutes) in enumerate(entries, start=1):
print(f"{number}. {activity}: {minutes} minutes")
elif choice == "3":
print("Goodbye!")
break
else:
print("Choose 1, 2, or 3.")
Add and display multiple activities
- Enter 1 at the menu prompt.
- Enter Reading at the activity prompt.
- Enter 25 at the minutes prompt.
- Enter 1 at the menu prompt again.
- Enter Walking at the activity prompt.
- Enter 30 at the minutes prompt.
- Enter 2 to display the activity log.
You'll see 1. Reading: 25 minutes followed by 2. Walking: 30 minutes.
You have the core interaction working. One run now keeps several activities available.
Restart the tracker and expose the gap
- Enter 3 at the menu prompt.
You'll see Goodbye! in the Shell. The program stops cleanly.
Before you restart the module, do you think the two activities will still appear?
- In the IDLE editor from earlier, choose Run Module.
- Enter 2 at the fresh menu prompt.
You'll see No activities yet. That empty log is the intended shortfall.
Why did the log reset?
The entries = [] line runs whenever the module begins. That line creates a fresh list.
The earlier activities lived only in runtime memory. Ending the run removed the only copy.
You found the tracker's memory limit through a real restart test. Next, you'll organize the menu logic into focused functions.
Refactor Into Functions
Your tracker can already collect several activities during one run. The menu currently handles every responsibility in one block.
In this step, you will use Python functions to give input, display, calculation, and menu control their own names. This structure keeps each part focused before file storage adds more logic.
In this step, get ready to:
- Move activity input into add_entry(entries).
- Move log display into show_entries(entries).
- Create focused functions for summary calculation and menu control.
Extract input and display functions
A function gives one responsibility a reusable name. A parameter lets that function work with the same entries list as the menu.
- In activity_tracker.py, place your cursor above entries = [].
- Add add_entry(entries) by pasting this code:
def add_entry(entries):
activity = input("Activity: ").strip()
minutes = int(input("Minutes: "))
entries.append((activity, minutes))
print("Activity added.")
What Does This Function Do?
- The entries parameter refers to the activity list supplied by the menu.
- The function collects an activity name plus its duration.
- The new tuple is appended to the supplied list.
- Save activity_tracker.py.
- In the IDLE editor menu, click Run.
- Click Run Module.
You will see the activity menu again. This confirms Python accepted the new function definition.
Seeing an Indentation Error?
- Check that the four lines inside add_entry(entries) have the same indentation.
- Keep def add_entry(entries): aligned with the left edge of the file.
- Help me fix the indentation in my add_entry(entries) function.
The function now exists. The menu still contains the original input code, so its first branch needs to delegate that work.
- Find if choice == "1": inside the existing menu loop.
- Select the four indented lines from the activity prompt through print("Activity added.").
- Replace those lines with this function call:
add_entry(entries)
Why Pass Entries In?
The call passes the menu's list into add_entry(entries). The function updates that same list in memory.
- Save activity_tracker.py.
- Choose Run Module from IDLE's Run menu.
- Enter 1 at the menu prompt.
- Enter Cycling at the activity prompt.
- Enter 30 at the minutes prompt.
You will see Activity added. in the Shell. The menu now reaches the extracted input function.
Activity Option Not Working?
- Confirm that add_entry(entries) is indented directly beneath if choice == "1":.
- Check that the function name uses the same spelling in its definition and call.
- Help me debug why option 1 does not call add_entry(entries).
Displaying the log is another complete responsibility. Extracting it keeps the empty-list check beside the numbered output it controls.
- Return to activity_tracker.py in the IDLE editor.
- Place your cursor after the blank lines beneath add_entry(entries).
- Add show_entries(entries) by pasting this code:
def show_entries(entries):
if not entries:
print("No activities yet.")
return
print("\nActivity log")
for number, (activity, minutes) in enumerate(entries, start=1):
print(f"{number}. {activity}: {minutes} minutes")
What Does This Function Do?
- The empty-list check prints a clear message when there is nothing to display.
- The return statement ends the function after that message.
- The for loop unpacks each tuple while number tracks its position.
- Save activity_tracker.py.
- Choose Run Module from IDLE's Run menu.
You will see the menu in the Shell again. The new display function is defined without syntax errors.
- Find elif choice == "2": inside the existing menu loop.
- Select its nested empty-list check plus its existing display logic.
- Replace the selected logic with this function call:
show_entries(entries)
How Does the Menu Stay Focused?
The second menu branch now chooses what should happen. The show_entries(entries) function owns how the log is displayed.
- Save activity_tracker.py.
- Choose Run Module from IDLE's Run menu.
- Enter 2 at the menu prompt.
You will see No activities yet. because this new run starts with an empty list. That result confirms option 2 reaches the display function.
Log Option Not Responding?
- Confirm that show_entries(entries) is indented directly beneath elif choice == "2":.
- Remove any leftover nested display code from that menu branch.
- Help me debug why option 2 does not reach show_entries(entries).
Add the summary and main functions
A summary turns the stored durations into one useful result. A dedicated main() function then owns the list plus the menu loop.
- Place your cursor after show_entries(entries).
- Add show_summary(entries) by pasting this code:
def show_summary(entries):
total_minutes = 0
for _, minutes in entries:
total_minutes += minutes
print(f"Total time: {total_minutes} minutes")
How Is the Total Calculated?
- The total_minutes variable starts at 0.
- The loop ignores each activity name with _ because the calculation only needs minutes.
- Each duration is added to the running total before the result is printed.
- Save activity_tracker.py.
- Choose Run Module from IDLE's Run menu.
You will see the existing menu again. This proves the summary function is defined correctly before the menu starts using it.
The remaining global code becomes main(). Its menu gains a summary option while the quit option moves to choice 4.
- Find the global entries = [] line beneath the three functions.
- Select everything from that line through the final else branch.
- Replace the selected code with the complete main() function below:
def main():
entries = []
while True:
print("\nActivity Tracker")
print("1. Add activity")
print("2. View activities")
print("3. Show summary")
print("4. Quit")
choice = input("Choose an option: ").strip()
if choice == "1":
add_entry(entries)
elif choice == "2":
show_entries(entries)
elif choice == "3":
show_summary(entries)
elif choice == "4":
print("Goodbye!")
break
else:
print("Choose 1, 2, 3, or 4.")
main()
What Does Main Control?
- The entries list now belongs to main().
- The while True loop keeps presenting the four menu choices.
- Each branch delegates one responsibility to a focused function.
- The final main() call starts the tracker.
- Save activity_tracker.py.
- Choose Run Module from IDLE's Run menu.
You will see a four-option menu in the Shell. Option 3 now displays the summary choice.
Menu Missing or Indented Incorrectly?
- Check that every menu line sits inside main() with consistent indentation.
- Keep the final main() call aligned with the left edge of the file.
- Help me fix the structure of my main() function.
Verify every menu path
Before you run the refactored tracker, what total do you expect from activities lasting 20 minutes plus 35 minutes?
- Choose Run Module from IDLE's Run menu.
- Enter 1 at the menu prompt.
- Enter Walking at the activity prompt.
- Enter 20 at the minutes prompt.
You will see Activity added. before the menu returns.
- Enter 1 at the menu prompt.
- Enter Reading at the activity prompt.
- Enter 35 at the minutes prompt.
- Enter 3 at the menu prompt.
You will see Total time: 55 minutes. The accumulator has added both durations correctly.
- Enter 2 at the menu prompt.
- Enter 4 after reviewing the numbered log.
You will see both activities in numbered order. The tracker then prints Goodbye! when it exits.
- Choose Run Module again from IDLE's Run menu.
- Enter 2 at the fresh menu prompt.
You will see No activities yet. because the refactor still stores entries only in memory. That limitation is ready for the next step.
Summary Total Incorrect?
- Confirm that total_minutes += minutes is indented inside the for loop.
- Check that both activities were added during the same run.
- Help me debug an incorrect total from show_summary(entries).
✔️ Awesome, I've got everything!
Your tracker now separates input, display, summary calculation, and menu control into focused functions. Double-check that you saved activity_tracker.py.
ⓧ I'd like to double check the full code
Compare your saved file with this complete in-memory version.
def add_entry(entries):
activity = input("Activity: ").strip()
minutes = int(input("Minutes: "))
entries.append((activity, minutes))
print("Activity added.")
def show_entries(entries):
if not entries:
print("No activities yet.")
return
print("\nActivity log")
for number, (activity, minutes) in enumerate(entries, start=1):
print(f"{number}. {activity}: {minutes} minutes")
def show_summary(entries):
total_minutes = 0
for _, minutes in entries:
total_minutes += minutes
print(f"Total time: {total_minutes} minutes")
def main():
entries = []
while True:
print("\nActivity Tracker")
print("1. Add activity")
print("2. View activities")
print("3. Show summary")
print("4. Quit")
choice = input("Choose an option: ").strip()
if choice == "1":
add_entry(entries)
elif choice == "2":
show_entries(entries)
elif choice == "3":
show_summary(entries)
elif choice == "4":
print("Goodbye!")
break
else:
print("Choose 1, 2, 3, or 4.")
main()
That refactor is working: each responsibility now has a focused function. Next, you will save activities to a text file so they return after a restart.
Save Activities to a File
Your tracker now uses focused functions for each menu job. However, a restart still clears every activity.
This step adds a text file that becomes the tracker's memory between runs. The script appends each new activity to disk.
When the tracker starts, it rebuilds the activity list from those saved lines.
In this step, get ready to:
- Save each new activity in a local text file.
- Load saved activities when the tracker starts.
- Prove persistence with a restart test.
Save each new activity
Append mode adds a new record without replacing older records. A pipe character separates the activity name from its duration.
- In the activity_tracker.py editor from earlier, place your cursor above def add_entry(entries):.
- Add the data filename plus the save function by pasting this code:
DATA_FILE = "python_activity_tracker.txt"
def save_entry(activity, minutes):
with open(DATA_FILE, "a", encoding="utf-8") as file:
file.write(f"{activity}|{minutes}\n")
What does this code do?
- The DATA_FILE constant gives the program one shared filename for its saved records.
- The save_entry() function receives one activity name plus one duration.
- File mode a appends each record to the end of the file. Existing records remain in place.
- The \n escape starts a new line after each record.
- Find the existing add_entry(entries) function.
- Replace that function with this version:
def add_entry(entries):
activity = input("Activity: ").strip()
minutes = int(input("Minutes: "))
entries.append((activity, minutes))
save_entry(activity, minutes)
print("Activity saved.")
How does saving fit into the function?
- The tuple still enters the in-memory entries list first.
- The save_entry(activity, minutes) call writes the same values to the data file.
- The final message appears after the file write completes.
- Save activity_tracker.py.
- In IDLE, choose Run Module.
- Enter 1 at the menu prompt.
- Enter Reading at the activity prompt.
- Enter 25 at the minutes prompt.
- Enter 4 at the next menu prompt.
The Shell shows Activity saved. before the menu returns. That is your first activity stored outside the running program.
Activity not saving?
Check that save_entry() sits outside add_entry(). Confirm that the call to save_entry(activity, minutes) appears after the tuple is appended.
Use this if the file still does not save: Help me debug why save_entry() is not writing my activity record.
Load saved activities at startup
Saving protects new records after they are entered. The tracker also needs to rebuild its list before showing the menu.
- Place your cursor between the DATA_FILE line and the save_entry() function.
- Add the loading function by pasting this code:
def load_entries():
entries = []
try:
with open(DATA_FILE, "r", encoding="utf-8") as file:
for line in file:
activity, minutes = line.strip().split("|")
entries.append((activity, int(minutes)))
except FileNotFoundError:
pass
return entries
How are the records rebuilt?
- The function starts with an empty entries list.
- File mode r reads the saved records one line at a time.
- The line.strip().split("|") expression removes the line ending. It separates the activity name from the saved duration.
- The int(minutes) conversion restores each duration as an integer.
- The FileNotFoundError handler lets the tracker start with an empty list before its first data file exists.
- In main(), find the line entries = [].
- Replace that line with this one:
entries = load_entries()
Why change this line?
The call to load_entries() prepares the activity list before the menu begins. Every saved tuple becomes available to the existing view and summary functions.
- Save activity_tracker.py.
Before you rerun the tracker, do you think the activity from the previous run returns or disappears?
- Choose Run Module.
- Enter 2 at the menu prompt.
You should see 1. Reading: 25 minutes in the activity log. The tracker has rebuilt that tuple from the saved file.
- Enter 4 to quit the tracker.
Saved activity missing?
Confirm that main() uses entries = load_entries(). Check that the delimiter inside split("|") matches the delimiter written by save_entry().
Use this for help with the loading path: Help me find why load_entries() is not restoring my saved activities.
Prove the activity log survives
One restored activity proves the reading path works. A second save followed by another restart tests the complete persistence cycle.
Before you repeat the restart test, do you expect the log to contain both records or only the newest record?
- Choose Run Module.
- Enter 1 at the menu prompt.
- Enter Walking at the activity prompt.
- Enter 30 at the minutes prompt.
- Enter 4 at the next menu prompt.
- Choose Run Module again.
- Enter 2 at the menu prompt.
You should see a numbered activity log containing Reading: 25 minutes plus Walking: 30 minutes.
That restored log confirms that python_activity_tracker.txt exists on disk. It also confirms that the program reads every saved record after a restart.
✔️ Awesome, I've got everything!
Your activity log now survives a full restart. Great work. The tracker has persistent local memory.
ⓧ I'd like to double check the full code
Your complete activity_tracker.py file should look like this:
DATA_FILE = "python_activity_tracker.txt"
def load_entries():
entries = []
try:
with open(DATA_FILE, "r", encoding="utf-8") as file:
for line in file:
activity, minutes = line.strip().split("|")
entries.append((activity, int(minutes)))
except FileNotFoundError:
pass
return entries
def save_entry(activity, minutes):
with open(DATA_FILE, "a", encoding="utf-8") as file:
file.write(f"{activity}|{minutes}\n")
def add_entry(entries):
activity = input("Activity: ").strip()
minutes = int(input("Minutes: "))
entries.append((activity, minutes))
save_entry(activity, minutes)
print("Activity saved.")
def show_entries(entries):
if not entries:
print("No activities yet.")
return
print("\nActivity log")
for number, (activity, minutes) in enumerate(entries, start=1):
print(f"{number}. {activity}: {minutes} minutes")
def show_summary(entries):
total_minutes = 0
for _, minutes in entries:
total_minutes += minutes
print(f"Total time: {total_minutes} minutes")
def main():
entries = load_entries()
while True:
print("\nActivity Tracker")
print("1. Add activity")
print("2. View activities")
print("3. Show summary")
print("4. Quit")
choice = input("Choose an option: ").strip()
if choice == "1":
add_entry(entries)
elif choice == "2":
show_entries(entries)
elif choice == "3":
show_summary(entries)
elif choice == "4":
print("Goodbye!")
break
else:
print("Choose 1, 2, 3, or 4.")
main()
This reference includes the loading helper, the saving helper, the updated add function, and the startup call inside main().
You have closed the tracker's biggest gap. Saved activities now outlive the program that created them.
Secret mission
Secret Mission: Reject Bad Input
Harden your persistent activity tracker with validation for names and minutes. Blank names, pipe characters, text durations, and non-positive numbers will be rejected before they can stop the program or reach the data file.
Clean Up Your Resources
Clean Up Your Resources
Everything in this project runs locally on your Windows device. There are no ongoing charges.
Decide whether to keep the tracker ready, pause your work, or delete its files.
Resources you used:
- The local activity_tracker.py application.
- The python_activity_tracker.txt data file created after your first valid activity was saved.
- A local Python 3.15.0 installation that includes IDLE 3.15.0 if setup was required.
Keep everything running
Leaving everything as it is keeps the tracker ready for your next activity.
- Leave activity_tracker.py in the folder where you saved it.
- Leave python_activity_tracker.txt in the same folder so your saved activities remain available.
- Keep Python 3.15.0 with IDLE installed for future projects.
Pause - I'll come back to this later
Pausing means closing IDLE while preserving your application and saved activities. Your local files remain unchanged without creating charges.
- Close the IDLE editor window.
- Close the IDLE Shell window.
- Leave activity_tracker.py in its saved folder.
- Leave python_activity_tracker.txt in the same folder so the tracker can load your activities when you return.
Delete - I don't want to use this again
Deleting gives you a clean slate on this device. You can also remove Python if you installed it only for this project.
- Close IDLE if it is still open.
- Return to the folder where you saved activity_tracker.py.
- Delete activity_tracker.py from that folder.
- Delete python_activity_tracker.txt from the same folder if it exists.
- Check the folder contents to confirm that neither project file remains.
- Remove Python 3.15.0 from Windows Installed apps if you installed it only for this project.
- Confirm that Python 3.15.0 no longer appears in Windows Installed apps if you chose to remove it.
Nice Work!
Nice Work!
You did it! Your persistent Python activity tracker records activities, calculates total time, and restores saved entries after every restart.
You've learned how to:
- Build a menu-driven activity tracker that collects each activity name plus its duration. Store each entry as a tuple inside a list.
- Use a while loop to keep the menu active. Use for loops to number entries. Use another loop to calculate total minutes. Refactor the tracker into focused functions.
- Add persistent text-file storage with python_activity_tracker.txt. Load saved records when the program starts. Handle a missing first-run file without crashing.
- Complete the optional Secret Mission with input validation. Reject blank names. Block names containing |. Reject durations below 1. Use exception handling to catch ValueError.
Ready to quiz yourself?