Claude Academy受講管理をPythonで催促対象だけ抽出する
研修は「全員にリマインドを送れば進む」と言われがちです。実際には、期限を過ぎた一覧を毎朝まとめて送るほど、受け取る側は自分が今日対応すべきか分からなくなります。
8月に日本語対応した Claude Academy を社内で案内するなら、受講ページの共有だけで終わらせない方がいいです。僕なら、期限当日、期限を3日過ぎた日、その後は7日おきにだけ催促対象を出します。送信まで自動化せず、まず対象者のCSVを作る。ここ地味に効きます。
今回は、GoogleスプレッドシートからダウンロードしたCSVを読み、催促する人だけを reminders.csv に書き出すPythonです。外部ライブラリは使いません。
先に決めた催促のルール
研修管理の表には、次の7列を置きます。1行目の列名はコードと同じにしてください。
name,email,course,due_date,completed_at,last_reminded_at,remind_count
佐藤,sato@example.com,Prompting 101,2026-08-25,,,0
田中,tanaka@example.com,Agent Basics,2026-08-22,,,0
鈴木,suzuki@example.com,Agent Basics,2026-08-21,,,1
due_date は YYYY-MM-DD です。完了したら completed_at に日付を入れます。催促を送った人は last_reminded_at と remind_count を更新します。
抽出する条件は4つだけです。
- 未完了である
- 期限日を過ぎている、または当日である
- 今日すでに催促していない
- 期限当日、期限の3日後、以後は7日おきのどれかである
3日後から始めるのは、期限翌日に連続で追いかけないためです。急ぎの資格や必須研修なら1日後に変えて構いませんが、通常の学習では間を空けた方が、催促文も事務的になりすぎません。
催促対象を reminders.csv に出すPython
import argparse
import csv
import datetime as dt
REQUIRED_COLUMNS = {
"name", "email", "course", "due_date", "completed_at",
"last_reminded_at", "remind_count",
}
def parse_date(value, field, row_number):
try:
return dt.date.fromisoformat(value)
except ValueError as error:
message = f"{row_number}行目の{field}は YYYY-MM-DD で入力してください: {value}"
raise ValueError(message) from error
def reminder_type(days_overdue):
if days_overdue == 0:
return "期限当日"
if days_overdue >= 3 and (days_overdue - 3) % 7 == 0:
return "期限後の定期確認"
return None
def select_reminders(rows, today):
selected = []
for row_number, row in enumerate(rows, start=2):
if row["completed_at"].strip():
continue
due_date = parse_date(row["due_date"].strip(), "due_date", row_number)
if due_date > today:
continue
if row["last_reminded_at"].strip() == today.isoformat():
continue
days_overdue = (today - due_date).days
kind = reminder_type(days_overdue)
if kind is None:
continue
selected.append({
"name": row["name"],
"email": row["email"],
"course": row["course"],
"due_date": due_date.isoformat(),
"days_overdue": days_overdue,
"reminder_type": kind,
"remind_count": row["remind_count"] or "0",
})
return selected
def load_rows(path):
with open(path, encoding="utf-8-sig", newline="") as file:
reader = csv.DictReader(file)
headers = set(reader.fieldnames or [])
missing = REQUIRED_COLUMNS - headers
if missing:
raise ValueError("CSVに不足している列: " + ", ".join(sorted(missing)))
return list(reader)
def write_rows(path, rows):
columns = [
"name", "email", "course", "due_date", "days_overdue",
"reminder_type", "remind_count",
]
with open(path, "w", encoding="utf-8-sig", newline="") as file:
writer = csv.DictWriter(file, fieldnames=columns)
writer.writeheader()
writer.writerows(rows)
parser = argparse.ArgumentParser()
parser.add_argument("--input", default="training.csv")
parser.add_argument("--output", default="reminders.csv")
parser.add_argument("--today", help="検証用の日付。例: 2026-08-25")
args = parser.parse_args()
today = dt.date.fromisoformat(args.today) if args.today else dt.date.today()
reminders = select_reminders(load_rows(args.input), today)
write_rows(args.output, reminders)
print(f"{len(reminders)}人を {args.output} に出力しました")
ファイル名を reminder_targets.py として保存し、スプレッドシートから書き出したCSVを同じフォルダに置きます。まずは日付を固定して動かすと、対象になる条件が見えます。
python3 reminder_targets.py --input training.csv --today 2026-08-25
手元では、期限が8月25日の佐藤さん、8月22日の田中さん、8月21日の鈴木さん、当日送信済みの高橋さん、完了済みの伊藤さんを入れて確認しました。出力対象は次の2人だけでした。
対象者:
佐藤: 期限当日 (0日超過)
田中: 期限後の定期確認 (3日超過)
期限4日後の鈴木さんは出ません。期限当日に送信済みの高橋さんも出ません。last_reminded_at があるだけではなく、days_overdue - 3 を7で割った余りまで見ているからです。
この順番なら、同じ日に二通出る事故も避けられます。
スプレッドシートへ戻すときの注意
このスクリプトはメールを送りません。reminders.csv を開いて本文を確認し、既存のメール差し込み送信やGmailの下書きに渡す役です。宛先を機械的に送信する前に、休職中の人や配属が変わった人を外せます。
送った後は、元の表の last_reminded_at を当日の日付にして、remind_count を1増やします。この更新を忘れると、翌朝も同じ人が候補に残ります。スプレッドシートだけで運用する場合も、ここは手で埋めずにフィルタビューで対象を確認してから更新するのが安全です。
Claude Academyのように学習コンテンツが増えると、案内文を良くすることに時間を使いがちです。でも実務で詰まるのは、誰にいつ声をかけるかでした。催促対象を先に絞れば、1通ごとの文章も相手の進み具合に合わせて書けます。受講管理を「未完了一覧」から「今日連絡する人の一覧」へ変えると、担当者の朝の判断がかなり軽くなります。
参考: Claude Academy 日本語ページ、Impress Watch: Claude Academyの日本語対応