第9章 · 実例 2

`click` で業務メモ CLI(add/list/search/stats/export)

第 08 章「アプリを作る ── CLI ツール、Flet、Flutter」の 2 番目の角度: サブコマンド構造を持った業務 CLI

章のどの主張に対応するか

CLI ツールの作成と配布: 1 時間で書いて、pip install で世界中の Python ユーザーに即配布。

CLI で動く処理を Flet で GUI 化する追加コスト: Flet ライブラリのインストール と数十行の追加コード、1 時間。Flutter で書き直すなら 1 ヶ月。

(章本文「実例: 数字で見る」より)

example-1 が「写真整理 CLI」(単一コマンド)、これは 5 つのサブコマンド を持つ業務 CLI を click で書く。CLI を「アプリ」として育てる入口。

やること

journal.py 1 ファイル / 約 100 行で:

journal add     "テキスト" --tag meeting     # メモ追加
journal list    [--limit N] [--tag タグ]     # 一覧
journal search  "キーワード"                  # 全文検索(LIKE)
journal stats                                 # タグ別集計(JSON)
journal export  --out out/journal.md          # Markdown エクスポート
journal --help                                # 自動生成のヘルプ

ストレージは SQLite 1 ファイル(out/journal.db)。 make all でデモ実行(8 件投入 → 一覧・検索・集計・エクスポート)。

構成

example-2/
├── README.md
├── journal.py    ── click ベースの CLI(約 100 行)
├── Makefile      ── デモシナリオ
├── results.md
└── out/
    ├── journal.db   ── SQLite データ
    ├── stats.json   ── stats サブコマンドの出力
    └── journal.md   ── export サブコマンドの出力

実行

pip install click
make clean && make all

各サブコマンドのヘルプは make help

なぜこれが「実例」になるのか

業務メモ・日報の世界には 3 つの選択肢がある:

選択肢 コスト 自由度
Notion / Evernote 月 $10〜 サービス依存・退会で消える
Word + フォルダ フォントずれ・検索しづらい ×
CLI + Markdown 0 円 ○ 全部自分のもの

このフォルダの CLI なら:

そしてこのまま:

これが章で言う「まず CLI、必要なら GUI に上げる」の具体形。

「Flet にする」の意味

このフォルダは Flet を使っていないが、Flet にする手順は:

import flet as ft
from journal import conn  # 既存ロジックを再利用

def main(page: ft.Page):
    text = ft.TextField(label="メモ")
    def add_cb(e):
        with conn() as c:
            c.execute("INSERT INTO notes (text) VALUES (?)", (text.value,))
        text.value = ""
        page.update()
    page.add(text, ft.ElevatedButton("追加", on_click=add_cb))

ft.app(target=main)

追加 30 行で GUI 完成。CLI のロジック(SQLite アクセス・スキーマ)は 完全に再利用される。書き直しは発生しない

これが章の「CLI を一段ずつ昇格させる」の意味。

配布

# 1. PyPI に上げる(pyproject.toml を 1 つ書くだけ)
pip install build twine
python3 -m build
twine upload dist/*

# 2. ユーザは
pip install your-journal
journal add "first note"

App Store の審査も Apple Developer Program も要らない。世界中の Python ユーザーに即配布


計測結果 — 第 08 章 example-2

実行環境: Linux 6.18 / Python 3.11 / click 8.x / SQLite 3.45

規模と性能

項目 数値
journal.py 行数 約 100 行(コメント込み)
依存パッケージ click のみ(SQLite は標準ライブラリ)
サブコマンド数 5 個 + --help
1 サブコマンドの実行時間 数 ms(Python 起動 + SQLite クエリ)
起動オーバーヘッド 約 50 ms(Python インタプリタ)

デモシナリオ(make all の出力)

1. メモを 8 件投入

$ journal add "山田農園と打ち合わせ。来月から週次納品" --tag meeting
  added #1: 山田農園と打ち合わせ。来月から週次納品
$ journal add "経費レポートを Claude に作らせた、20 分で完了" --tag review
  added #2: 経費レポートを Claude に作らせた、20 分で完了
...(計 8 件)

2. 一覧(最新 5 件)

$ journal list --limit 5
    8  2026-05-05 06:11  経理: 4 月分の領収書を Claude に分類させた、3 分
    7  2026-05-05 06:11  Mochi.ai の LP に「365 日返金保証」を追記
    6  2026-05-05 06:11  [todo] 山田農園に来週納品手配のメールを送る
    5  2026-05-05 06:11  今月の MRR が目標に到達
    4  2026-05-05 06:11  [ops] サーバ再起動 04:00 完了、ダウンタイム 12 秒

3. タグ絞り込み

$ journal list --tag meeting
    1  2026-05-05 06:11  [meeting] 山田農園と打ち合わせ。来月から週次納品

4. 全文検索

$ journal search 山田
  '山田' に一致: 2 件
      6  2026-05-05  山田農園に来週納品手配のメールを送る
      1  2026-05-05  山田農園と打ち合わせ。来月から週次納品

5. タグ別集計(JSON, out/stats.json)

{
  "total": 8,
  "by_tag": [
    {"tag": "(no-tag)", "n": 3},
    {"tag": "todo",     "n": 1},
    {"tag": "review",   "n": 1},
    {"tag": "ops",      "n": 1},
    {"tag": "meeting",  "n": 1},
    {"tag": "lead",     "n": 1}
  ]
}

JSON で出すので、cron で集計 → Slack に投げる、なども容易。

6. Markdown エクスポート(out/journal.md)

# 業務日報

件数: 8

## 2026-05-05

- 06:11  `meeting` 山田農園と打ち合わせ。来月から週次納品
- 06:11  `review` 経費レポートを Claude に作らせた、20 分で完了
- 06:11  `lead` 新規顧客 C016 が問い合わせ、料金で要確認
- 06:11  `ops` サーバ再起動 04:00 完了、ダウンタイム 12 秒
...

そのまま月次報告に貼り付けられる Markdown が出る。

7. 自動生成のヘルプ

$ journal --help
Usage: journal.py [OPTIONS] COMMAND [ARGS]...

  一行メモから月次報告まで、ひとつの CLI で。

Commands:
  add     メモを追加。
  export  Markdown 形式でエクスポート(月次報告用)。
  list    最新のメモを一覧。
  search  全文検索(LIKE)。
  stats   件数・タグ別集計を JSON で。

@click.command() のデコレータと docstring から 自動生成--help を書く必要がない。

CLI vs SaaS(章本文との対応)

項目 Notion / Evernote この CLI
月額 $10〜 0 円
データの所有権 サービス側 自分
検索 数 ms(クラウド) 数 ms(SQLite)
エクスポート 限定的 Markdown 完全
AI 連携 限定的 journal.py を Claude に渡せる
サービス終了で消える? 消える 消えない

来月の運用

cron に登録:

0 9 * * 1   journal export --since "$(date -d 'last week' +%Y-%m-%d)" --out /tmp/weekly.md

毎週月曜 9:00 に「先週の業務日報」が /tmp/weekly.md に出る。それを Claude に「これを月次報告用に要約して」と渡せば、月次報告も自動。

再現手順

pip install click
make clean && make all

実行は数秒で完了。

ファイル一覧

out/

第9章「アプリを作る ── CLIツール、Fletアプリ、Flutterアプリ」に戻る