🌱
Basic
Review · Integration · File persistence
Week 10 — Basic Capstone: CLI To-Do App
Combine everything from weeks 1–9 into a full command-line to-do application with add, list, complete, and delete commands. Optionally persist tasks to a JSON file.
capstoneprojectCLIJSONintegration
Duration
⏱ 3 hours
Level
📊 Beginner
Prerequisite
🎯 Weeks 1–9
OUTCOME
A working CLI to-do app you can run from the terminal
What you'll learn
- 1Integrate lists, dicts, functions, and loops in one project
- 2Handle user input robustly with error messages
- 3Save and load tasks from a JSON file
- 4Structure code into well-named functions
- 5Practice the full development loop: plan → code → test
1. Project Specification
- Commands: add <title>, list, done <id>, delete <id>, quit
- Each task: id (auto-increment), title, done (bool)
- Persist tasks in tasks.json on disk
- Show task count and completion stats on list
2. Architecture
- load_tasks() / save_tasks() — JSON file I/O
- add_task(tasks, title) — append new task dict
- list_tasks(tasks) — print numbered table
- mark_done(tasks, task_id) — find by id and set done=True
- delete_task(tasks, task_id) — filter out by id
- main() — REPL loop with command dispatch
3. Sample Session
text
Todo App — type 'help' for commands
> add Buy groceries
[1] added.
> add Write report
[2] added.
> list
ID Status Title
1 [ ] Buy groceries
2 [ ] Write report
> done 1
[1] marked as done.
> list
ID Status Title
1 [x] Buy groceries
2 [ ] Write report
Done: 1/2
> quit
Bye!4. Extension Challenges
- Add a priority field (high/medium/low) and sort by priority on list
- Add a due date field and highlight overdue tasks in red (colorama)
- Add search <keyword> command to filter tasks
💻 Examples
Run these examples and check the output yourself.
todo.py— Complete to-do app implementation
CODE
import json, os
FILE = "tasks.json"
def load_tasks():
if os.path.exists(FILE):
with open(FILE) as f:
return json.load(f)
return []
def save_tasks(tasks):
with open(FILE, "w") as f:
json.dump(tasks, f, indent=2)
def add_task(tasks, title):
task_id = max((t["id"] for t in tasks), default=0) + 1
tasks.append({"id": task_id, "title": title, "done": False})
print(f" [{task_id}] added.")
def list_tasks(tasks):
if not tasks:
print(" No tasks.")
return
print(f" {'ID':<4} {'Status':<8} Title")
for t in tasks:
status = "[x]" if t["done"] else "[ ]"
print(f" {t['id']:<4} {status:<8} {t['title']}")
done = sum(1 for t in tasks if t["done"])
print(f" Done: {done}/{len(tasks)}")
def mark_done(tasks, task_id):
for t in tasks:
if t["id"] == task_id:
t["done"] = True
print(f" [{task_id}] marked as done.")
return
print(f" Task {task_id} not found.")
def delete_task(tasks, task_id):
orig = len(tasks)
tasks[:] = [t for t in tasks if t["id"] != task_id]
if len(tasks) < orig:
print(f" [{task_id}] deleted.")
else:
print(f" Task {task_id} not found.")
def main():
tasks = load_tasks()
print("Todo App — type 'help' for commands")
while True:
line = input("> ").strip()
if not line:
continue
parts = line.split(maxsplit=1)
cmd = parts[0].lower()
arg = parts[1] if len(parts) > 1 else ""
if cmd == "add":
if arg:
add_task(tasks, arg)
save_tasks(tasks)
else:
print(" Usage: add <title>")
elif cmd == "list":
list_tasks(tasks)
elif cmd == "done":
try:
mark_done(tasks, int(arg))
save_tasks(tasks)
except ValueError:
print(" Usage: done <id>")
elif cmd == "delete":
try:
delete_task(tasks, int(arg))
save_tasks(tasks)
except ValueError:
print(" Usage: delete <id>")
elif cmd in ("quit", "exit"):
print(" Bye!")
break
elif cmd == "help":
print(" Commands: add <title>, list, done <id>, delete <id>, quit")
else:
print(f" Unknown command: {cmd}")
if __name__ == "__main__":
main()
📝 Exercises
Try them yourself first, then open the solution to compare.
Exercise 1
Implement the base app
Goal: Implement the to-do app as described with add, list, done, delete, and quit.
Requirements
- All 5 commands work correctly
- Tasks persist across runs (JSON file)
- Invalid input shows helpful error messages
Grading
- · add/list work — 30%
- · done/delete work — 30%
- · JSON persistence — 30%
- · Error handling — 10%
Exercise 2
Add priority support
Goal: Extend the app with a priority field (high/medium/low).
Requirements
- add command accepts optional priority: add <title> [--priority high]
- list sorts by priority: high → medium → low
- Priority shown in list output
Example code / lecture materials
All lecture materials and example code are openly available on GitHub.
View on GitHub ↗