Python Series track

Week 10 · Intermediate Track

Week 10 — Intermediate Capstone: Text Analysis Tool

Combine the intermediate track skills to build a command-line text analysis tool that reads files, tokenizes text, computes statistics, and produces a formatted report.

Intermediate Track1 min readFree lesson

Stage: Intermediate | Prerequisite: All of weeks 1~9

Learning Goals

  • Complete a mini project using all OOP, exceptions, and file I/O
  • Apply module separation and package structure

1. Considerations When Designing the Project

  1. Data Model — Which classes will represent it?
  2. Persistent Storage — JSON? CSV? In what format?
  3. User Interface — Command-based? Menu-based?
  4. Module Separation — models.py, storage.py, cli.py, etc.
my_app/
├── __init__.py
├── models.py       # Class definitions (Word, Book, etc.)
├── storage.py      # JSON Save/Load
├── cli.py          # Main loop, command handling
└── main.py         # Entry point

3. Skeleton Example — Flashcard App

Refer to examples/wordbook_skeleton/.

Key Examples (examples/)

File Content
wordbook_skeleton/models.py Word, WordBook classes
wordbook_skeleton/main.py Entry point

⚠️ Common Mistakes

  1. All code in a single file main.py — Without separation, if it exceeds 200 lines it becomes a maze
  2. Objects that can't be JSON serialized — Use dataclass + asdict()
  3. Incorrect timing of saving — Save on every change vs on exit — Decide policy
  4. Exceptions too narrowly or too broadly used — Only use except where recovery is possible

❓ FAQ

Q1. Can we use dataclass? A. Recommended. Simple data classes are convenient with dataclass. (Explained in detail in Advanced Week 1)

Q2. Single file vs package? A. Single file only if under 50 lines. If more than that, separate.

📝 Final Assignment (exercises/)

Select 1 out of the following 4 options.

  1. exercise1.md — Vocabulary App (CRUD + JSON + Quiz Mode)
  2. exercise2.md — Simple Household Ledger (Income/Expenditure + JSON + Category)
  3. exercise3.md — Log Analyzer (Split with Regular Expressions + CSV Report)
  4. exercise4.md — Contact Manager (OOP) (Contact, AddressBook classes)

Submission

  • Modular package structure
  • Exception handling, user-friendly messages
  • README.md (How to run, How to use)

Example files

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

examples/wordbook_skeleton/main.pyPython
"""Vocabulary App — Entry Point"""
from pathlib import Path
from models import WordBook

def main():
    book = WordBook(Path("words.json"))
    while True:
        cmd = input("Command (add/list/del/quiz/quit):").strip()
        if cmd == "quit":
            break
        elif cmd == "add":
            en = input("English:")
            ko = input("Meaning:")
            book.add(en, ko)
        elif cmd == "list":
            for w in book.words:
                print(f"  {w.en} - {w.ko}")
        elif cmd == "del":
            en = input("English to delete:")
            ok = book.remove(en)
            print("Deleted" if ok else "None")
        elif cmd == "quiz":
            score, total = book.quiz(5)
            print(f"Score: {score} / {total}")

if __name__ == "__main__":
    main()
examples/wordbook_skeleton/models.pyPython
"""Vocabulary App — Model Definition"""
from dataclasses import dataclass, asdict
from pathlib import Path
import json
import random

@dataclass
class Word:
    en: str
    ko: str

class WordBook:
    def __init__(self, path: Path):
        self.path = path
        self.words: list[Word] = self._load()

    def _load(self):
        if not self.path.exists():
            return []
        data = json.loads(self.path.read_text(encoding="utf-8"))
        return [Word(**d) for d in data]

    def save(self):
        data = [asdict(w) for w in self.words]
        self.path.write_text(
            json.dumps(data, ensure_ascii=False, indent=2),
            encoding="utf-8"
        )

    def add(self, en, ko):
        self.words.append(Word(en, ko))
        self.save()

    def remove(self, en):
        before = len(self.words)
        self.words = [w for w in self.words if w.en != en]
        self.save()
        return before - len(self.words) > 0

    def quiz(self, count=5):
        if not self.words:
            return 0, 0
        score = 0
        questions = random.sample(self.words, min(count, len(self.words)))
        for i, w in enumerate(questions, 1):
            ans = input(f"[{i}/{len(questions)}] {w.en}? ").strip()
            if ans == w.ko:
                print("Correct!")
                score += 1
            else:
                print(f"Incorrect. Correct answer: {w.ko}")
        return score, len(questions)

Assignment

Requirements

  • Package structure: models.py, storage.py, cli.py, main.py
  • CRUD: words Add/Delete/Search/View
  • JSON persistent storage
  • Quiz mode (random N questions)
  • Bidirectional quiz (English→Korean, Korean→English)

Technical requirements

  • Use dataclass
  • Exception handling (file not found, JSON corruption)
  • Separate at least 5 functions

Final Project 2. Simple Household Account Book

Requirements

  • Transaction class (type, amount, category, memo, date)
  • Ledger class (persistent storage, aggregation)
  • Commands: add, list, summary, month <YYYY-MM>
  • Bar graph by category (# text)

Final Project 3. Log Analyzer

Requirements

  • Input: text log file
  • Separate [LEVEL] [DATE] [TIME] [MSG] using regex
  • Output: CSV report (level, date, time, msg)
  • Statistics: count by level

Final Project 4. Contact Manager (OOP)

Requirements

  • Contact class (name, phone, email, tags)
  • AddressBook class (CRUD, search, tag filter)
  • JSON persistent storage
  • Validate Phone-number/Email format using regex
Show the reference solution

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

solution1_wordbook.pyPython
"""Vocabulary Answers — Single File Version (It is actually recommended to separate into a package)"""
import json
import random
from dataclasses import dataclass, asdict
from pathlib import Path

DB = Path("words_solution.json")

@dataclass
class Word:
    en: str
    ko: str

def load():
    if not DB.exists():
        return []
    return [Word(**d) for d in json.loads(DB.read_text(encoding="utf-8"))]

def save(words):
    DB.write_text(
        json.dumps([asdict(w) for w in words], ensure_ascii=False, indent=2),
        encoding="utf-8"
    )

def main():
    words = load()
    print(f"Words: {len(words)}")
    while True:
        cmd = input("\n> ").strip().lower()
        if cmd == "quit":
            save(words); break
        elif cmd == "add":
            en = input("English:")
            ko = input("Meaning:")
            words.append(Word(en, ko)); save(words)
        elif cmd == "list":
            for w in words: print(f"  {w.en}: {w.ko}")
        elif cmd == "quiz":
            n = min(5, len(words))
            if n == 0:
                print("No words"); continue
            sample = random.sample(words, n)
            score = 0
            for i, w in enumerate(sample, 1):
                a = input(f"[{i}/{n}] {w.en}? ")
                if a == w.ko:
                    score += 1; print("Answer")
                else:
                    print(f"Incorrect. Correct answer: {w.ko}")
            print(f"Score: {score}/{n}")

if __name__ == "__main__":
    main()

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