Python Series track

Week 10 · Basic Track

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.

Basic Track2 min readFree lesson

Level: Basic | Players: Weeks 1–9 all

Learning Goals

  • Combine everything learned so far to complete a small program
  • Naturally combines input, conditions, loops, data structures, and functions
  • Gain experience in debugging and code organization

1. Project Design Procedure

  1. Organize Requirements — Write down on paper what you are making and what the input/output is
  2. Deciding on Data Structures — List? Dictionary? Both?
  3. Function Separation — Into small units (input, processing, output, etc.)
  4. Flow Writing — main loop, termination conditions
  5. Build gradually — Don’t code everything at once; check the functionality one feature at a time

2. Basics of Debugging

print debugging

The simplest and most powerful way.

def calc(x, y):
    print(f"DEBUG: x={x}, y={y}")
    result = x / y
    print(f"DEBUG: result={result}")
    return result

Reading Error Messages

The single line at the very bottom is key.

Traceback (most recent call last):
  File "main.py", line 10, in <module>
    print(scores[5])
IndexError: list index out of range

→ "Index out of range in the list at line 10"

3. Skeleton Example — Mini To-Do List

Refer to examples/todo_skeleton.py.

Key Examples (examples/)

File Content
todo_skeleton.py To-Do List Skeleton
debug_demo.py print debugging / try-except tasting

⚠️ Common Mistakes

  1. Trying to implement all features at once — Break it down into small steps
  2. A single block of code without function separation — hard to read and difficult to debug
  3. Hide the error as try: ... except: pass — Don’t know what went wrong or where
  4. Variable names as a, b, temp — meaningful names (scores, user_input)

❓ FAQ

Q1. I don't know where to start. A. "What appears on the screen when the program starts" → "What the user inputs" → write that flow directly into code.

Q2. What should I do if I get stuck? A. Check data by printing each line. If that still doesn't work, search (StackOverflow). If that still doesn't work, ask questions.

📝 Final Assignment (exercises/)

Choose one of the following 4 and complete it thoroughly.

  1. exercise1.md — Console To-Do List (Add/View/Complete/Delete)
  2. exercise2.md — Vocabulary Quiz (English-Korean mapping)
  3. exercise3.md — Simple Household Account Book (Income/Expense/Balance)
  4. exercise4.md — Number Baseball Game (Strike/Ball)

Submission

  • .py source file
  • Short README (how to run, syntax used)
  • Demo screenshot or recording

Example files

The runnable files used in this lesson. Copy them into your own project and experiment.

examples/debug_demo.pyPython
"""A taste of print debugging and simple try-except"""

def divide(a, b):
    print(f"DEBUG: divide called a={a}, b={b}")
    try:
        result = a / b
    except ZeroDivisionError:
        print("ERROR: Cannot divide by 0")
        return None
    print(f"DEBUG: result {result}")
    return result

print(divide(10, 2))
print(divide(10, 0))
print(divide(7, 3))
examples/todo_skeleton.pyPython
"""Last week of the basic course — To-Do List framework
Try expanding this code to support all add/done/del/quit commands."""

todos = []

def add(task):
    todos.append({"task": task, "done": False})

def show():
    if not todos:
        print("(nothing to do)")
        return
    for i, t in enumerate(todos, 1):
        mark = "[x]" if t["done"] else "[ ]"
        print(f"{i}. {mark} {t['task']}")

def done(idx):
    if 1 <= idx <= len(todos):
        todos[idx - 1]["done"] = True

#main loop
while True:
    cmd = input("Command (add/show/done/quit):").strip()
    if cmd == "quit":
        break
    elif cmd == "add":
        task = input("To-do:")
        add(task)
    elif cmd == "show":
        show()
    elif cmd == "done":
        idx = int(input("Number:"))
        done(idx)
    else:
        print("Unknown command")

Assignment

Goal

A console program where all commands Add/View/Complete/Delete work.

Requirements

  • Data structure: combination of list + dictionary
  • Commands: add, list, done <number>, del <number>, quit
  • Handle invalid commands / non-existent numbers

Input/Output Example

> add Buy milk
Added

> list
1. [ ] Buy milk
2. [x] Read book

> done 1
Item 1 completed

> del 2
Delete item 2

Grading

  • All 5 commands work — 60%
  • Handle invalid input — 20%
  • Code organization (function separation) — 20%

Final Assignment 2. Vocabulary Quiz

Goal

Issues random quizzes using an English-Korean vocabulary list.

Requirements

  • Vocabulary list as a dictionary in the code (10 or more items)
  • 10 questions (duplicates allowed)
  • Immediate feedback Correct/Wrong
  • Display score at the end

Input/Output Example

[1/10] What is the meaning of 'apple'? Apple
Correct!
[2/10] What is the meaning of 'book'? Bag
Incorrect. Correct answer: Book
...
Total score: 7 / 10

Final Assignment 3. Simple Household Ledger

Goal

Income/Expenditure records and shows balance and totals by category.

Requirements

  • Data structure: [{"type": "income"|"expense", "amount": int, "category": str, "memo": str}, ...]
  • Commands: add, list, summary, quit
  • Summary: total income, total expense, balance, expenses by category

Input/Output Example

> add
Income/Expenditure: Expense
Amount: 5000
Category: Food
Memo: Lunch
Added

> summary
Income: 100,000 KRW
Expense: 35,000 KRW
Balance: 65,000 KRW

[By Expense Category]
Food: 20,000 KRW
Transportation: 10,000 KRW
Others: 5,000 KRW

Final Assignment 4. Number Baseball

Goal

The user guesses a 3-digit number (no duplicates) generated by the computer.

Rules

  • 3-digit number (each digit 0~9, all different)
  • After the user guesses, if both position and value match, it's a strike; if only the value matches, it's a ball
  • If it's 3 strikes, it's correct

Input/Output Example

Enter a number (3 digits): 123
0 strikes 1 ball

Enter a number (3 digits): 456
1 strike 0 balls

...

Enter a number: 489
3 strikes! That's correct (7 attempts)
Show the reference solution

Try the assignment yourself first — comparing your attempt with a working solution is where most of the learning happens.

solution1.pyPython
"""Final Assignment 1 — Console To-Do List Solution"""

todos = []

def add(task):
    todos.append({"task": task, "done": False})

def show():
    if not todos:
        print("(nothing to do)")
        return
    for i, t in enumerate(todos, 1):
        mark = "[x]" if t["done"] else "[ ]"
        print(f"{i}. {mark} {t['task']}")

def mark_done(idx):
    if 1 <= idx <= len(todos):
        todos[idx - 1]["done"] = True
        print(f"Completed number {idx}")
    else:
        print("No such number")

def remove(idx):
    if 1 <= idx <= len(todos):
        del todos[idx - 1]
        print(f"Deleted number {idx}")
    else:
        print("No such number")

while True:
    raw = input("\n> ").strip()
    if not raw:
        continue
    parts = raw.split(maxsplit=1)
    cmd = parts[0]
    arg = parts[1] if len(parts) > 1 else ""

    if cmd == "quit":
        break
    elif cmd == "add":
        if arg:
            add(arg)
            print("Added")
        else:
            print("Please enter a task")
    elif cmd == "list":
        show()
    elif cmd == "done":
        try:
            mark_done(int(arg))
        except ValueError:
            print("Please enter a number")
    elif cmd == "del":
        try:
            remove(int(arg))
        except ValueError:
            print("Please enter a number")
    else:
        print("Unknown command")
solution2.pyPython
"""Final Assignment 2 — Vocabulary Quiz Answers"""
import random

WORDS = {
    "apple": "Apple", "book": "Book", "table": "Table", "door": "Door",
    "water": "Water", "fire": "Fire", "tree": "Tree", "sky": "Sky",
    "love": "Love", "time": "Time",
}

QUESTIONS = 10
score = 0

for i in range(1, QUESTIONS + 1):
    word, meaning = random.choice(list(WORDS.items()))
    answer = input(f"[{i}/{QUESTIONS}] What is the meaning of {word}?").strip()
    if answer == meaning:
        print("Correct!")
        score += 1
    else:
        print(f"Wrong answer. Correct answer: {meaning}")

print(f"Total score: {score} / {QUESTIONS}")
solution3.pyPython
"""Final Assignment 3 — Household Ledger Answers"""

records = []

def add():
    t = input("Income/Expense:").strip()
    if t not in ("Income", "Expense"):
        print("Only 'Income' or 'Expense' allowed"); return
    amount = int(input("Amount:"))
    category = input("Category:").strip()
    memo = input("Memo:").strip()
    records.append({"type": t, "amount": amount, "category": category, "memo": memo})
    print("Added")

def show():
    for i, r in enumerate(records, 1):
        sign = "+" if r["type"] == "Income" else "-"
        print(f"{i}. {sign}{r['amount']:,} KRW [{r['category']}] {r['memo']}")

def summary():
    income = sum(r["amount"] for r in records if r["type"] == "Income")
    expense = sum(r["amount"] for r in records if r["type"] == "Expense")
    print(f"Income: {income:,} KRW")
    print(f"Expense: {expense:,} KRW")
    print(f"Balance: {income - expense:,} KRW")

    by_cat = {}
    for r in records:
        if r["type"] == "Expense":
            by_cat[r["category"]] = by_cat.get(r["category"], 0) + r["amount"]
    if by_cat:
        print("[By Expense Category]")
        for cat, amt in sorted(by_cat.items(), key=lambda x: -x[1]):
            print(f"{cat}: {amt:,} KRW")

while True:
    cmd = input("Command (add/list/summary/quit):").strip()
    if cmd == "quit": break
    elif cmd == "add": add()
    elif cmd == "list": show()
    elif cmd == "summary": summary()
solution4.pyPython
"""Final Assignment 4 — Number Baseball Answers"""
import random

#3-digit number with no repeats from 0 to 9
digits = random.sample(range(10), 3)
answer = "".join(str(d) for d in digits)
tries = 0

print("3-digit number baseball start! (no repeats)")
while True:
    guess = input("Enter number (3 digits):").strip()
    if len(guess) != 3 or not guess.isdigit() or len(set(guess)) != 3:
        print("Enter only 3-digit numbers with no repeats")
        continue
    tries += 1
    strike = sum(1 for i in range(3) if guess[i] == answer[i])
    ball = sum(1 for i in range(3) if guess[i] in answer and guess[i] != answer[i])
    if strike == 3:
        print(f"3 strikes! Correct answer (attempt {tries})")
        break
    print(f"{strike} strikes {ball} balls")

Learn this lesson with a mentor

Work through the Python Series track on your own, or join a guided cohort at our Pitampura campus and online.

Call NowEnroll Now