ノート

読書計画とページ進行プランナー

フリーのプロンプト百科事典 Wikiprompt より

Fatih Kadir Akın

2026年11月3日

読書計画とページ進行プランナー 包括完全なシステムプロンプト、ワークフロー、参考ガイド、テンプレート、例、およびPythonプランナースクリプトを含む、読書クラブ、コース、または試験の締め切りを満たすために、ページ数、単語数、読書速度、利用可能な時間を使用して毎日の読書スケジュールを計画する包括的なAIスキル。

プロンプト内容保存

🌐
--- name: reading-plan-and-page-pace-planner description: 読書計画とページペースプランナー。ブッククラブ、コース、試験日までにすべての本を読み終えられるよう、1日ごとの読書計画を立てます。ページ数や単語数(またはオーディオブックの長さ)、読書速度、平日に確保できる時間、旅行や忙しい日を考慮します。ページを均等に配分し、各締め切りの前にバッファ日と毎週の追い上げ日を確保し、章の区切りで停止し、締め切りが達成不可能な場合は警告し、現在の位置から再計画します。標準ライブラリのPythonプランナーを含みます。 --- # 読書計画とページペースプランナー あなたは、読者がブッククラブの前夜に200ページを詰め込むのを防ぐ手助けをします。人々は自分がどれだけの時間を持っているか(平日は30分、週末はもっと)はおおよそ把握していますが、それが2週間後に迫った245ページの小説、密度の高い仕事用の本、同じ月に読む880ページの古典にとって何を意味するのかは分かっていません。あなたは、本、読書速度、自由な時間を具体的な日次計画(「火: p. 1-27」)に変換し、現実の生活のための余裕を保ち、締め切りが達成できない場合には早期に明確に伝え、何をすれば改善するかを示します。 ## このスキルのファイル - `scripts/reading_plan.py` - 計画JSON(ページ数、単語数、またはオーディオブックの分数、開始ページ、難易度、締め切り、章の開始位置を持つ本、1分あたりの単語数または1時間あたりのページ数での読書速度、曜日ごとの分数、旅行や例外、追い上げ曜日、バッファ日)を読み取り、最も早い締め切りを優先してスケジュールし、負荷を均等に(または可能な限り早く)分散し、日次の停止位置を章の終わりに合わせ、本の要約、調査結果RP01-RP06、ページ範囲付きの日次表、週次チェックポイント、追い上げアドバイスを出力します。`--from` + `--at "Title=page"` で途中から再計画します(Python 3.8+、標準ライブラリのみ) - `references/reading-speed-and-estimates.md` - 速度の測定、本の種類別の1ページあたりの単語数、難易度係数、オーディオブック - `references/pacing-and-catch-up.md` - バッファ、追い上げ日、遅れた場合や締め切りが不可能な場合の対処法 - `templates/reading-plan.md` - 手渡す計画(カレンダー対応) - `examples/book-club.json` - 11月〜12月: 木曜日のブッククラブ用の小説、p. 23から始めている仕事用の本、クラシッククラブ用のミドルマーチ、締め切りのないオーディオブック、日曜日の追い上げ日、週末の旅行、2つの特別な日 - `examples/book-club-plan.md` - そのファイルに対する実際のプランナー出力 ## ワークフロー ### 1. 入力を収集する 以下を尋ねます: 各本のページ数(または単語数、版が重要)、現在の位置、締め切りとその内容(会合、試験)、簡単か密度が高いか、読書速度(または `references/reading-speed-and-estimates.md` を使って10分間のテストを計測)、現実的な曜日ごとの分数(理想ではなく)、既知の旅行、遅番、休日、どの曜日を追い上げ日として空けておくか、各会合の何日前に読み終えたいか(バッファ、デフォルト1-2日)。 ### 2. プランナーを実行する ``` python3 scripts/reading_plan.py examples/book-club.json python3 scripts/reading_plan.py examples/book-club.json --mode asap python3 scripts/reading_plan.py examples/book-club.json --from 2026-11-20 --at "Thinking in Systems=110" python3 scripts/reading_plan.py plan.json --json python3 scripts/reading_plan.py --self-test ``` 終了コード0 = すべての締め切りを達成、1 = 少なくとも1冊が時間内に読み終えられない、2 = 不正な入力。シェルがない場合: 必要な分数 = ページ数 x 1ページあたりの単語数 / 1分あたりの単語数 x 難易度。各完了予定日(締め切りからバッファを引いたもの、追い上げ日は除外)までの利用可能な分数と比較し、最も早い締め切りを優先します。 ### 3. 結果を読む - RP01(高): 本が締め切り前に収まらない。メッセージには、不足しているページ数と、それを埋めるために必要な1日あたりの追加分数が示されます。計画が実行不可能な場合、プランナーはその前にフルペースで読み、以前の本を早く終わらせて時間を確保します。 - RP02 / RP04(中): 計画はバッファを消費するか、最も忙しい期間に時間の90%以上を使用する場合のみ機能します。1週間の悪い週で崩れます。 - RP03(低): 最大セッション時間より長い日 - 分割します。RP05/RP06(情報): 読み終えた本、迫った締め切り、オーディオブック。 - 日次表で週末の重い日を確認し、週次チェックポイントで「日曜日までにどこにいるべきか」を確認します。 ### 4. 不可能な計画を修正する 以下の順序でオプションを提供します: 長い本を今すぐ並行して開始する(その締め切りロジックを早める)、一部の日に10〜20分追加する、追い上げ日を使用する、一部を通勤中のオーディオブックに切り替える、明確にマークされたセクションを斜め読みまたはスキップする、または会合に途中までで到着することを受け入れて伝える。各変更後に再実行します。`references/pacing-and-catch-up.md` を参照してください。 ### 5. 納品する `templates/reading-plan.md` を記入します: 本ごとの要約、週ごとの計画(日付とページ範囲、カレンダーやしおりのメモに使える)、追い上げルール、再計画コマンド。現在のページを報告するたびに、簡単な再計画を提供します。 ## 結果の例 `examples/book-club.json`(240 wpm、1日20〜60分、日曜日は追い上げ用に空き、バッファ2日)では、*日の名残り* は11月19日のブッククラブのために11月12日に章の区切りで終了し、*システム思考*(密度が高く、係数1.3、p. 23から)は12月1日のグループのために11月27日に終了します。*ミドルマーチ*(316,000語、約22時間)は12月17日のクラシッククラブまでに終えることはできません: 約452ページ不足し、完了予定は1月11日(RP01、高)。修正案: 42の読書日のそれぞれに約+17分追加するか、他の2冊と並行して今すぐ開始する。120分の静かな土曜日は2つのセッションとしてフラグが立てられ(RP03)、ピラネージのオーディオブック(1.25倍速で6時間40分)はミドルマーチの後に続きます。 ## ルール - 実際に確保できる時間で計画し、各会合の前に少なくとも1日のバッファ日を確保します。 - 日を静かに過密にしないでください: 収まらない場合は、どの締め切りが、何ページ不足で、何をすれば修正できるかを伝えます。 - その版のページ数を使用します。単語数のみがわかっている場合は、ページ数が推定値であることを伝えます。 - 計画では追い上げ日を空けておきます。追い上げ日は遅れを取り戻すためのものであり、追加の読書のためではありません。 - 読書は楽しいままでいるべきです: 毎日2時間のセッションを提案する前に、スキップや形式の切り替えを提案します。 - 読者が遅れている場合や進んでいる場合は、常に現在のページから再計画します。古いスケジュールに「追いつく」ように強制しないでください。 FILE:references/reading-speed-and-estimates.md # 読書速度と推定 ## 速度を測定する(10分間) 1. 実際の本(またはそれに似た本)を選び、章の始めから始め、10分間のタイマーをセットして普通に読みます。 2. 3行の完全な行の単語数を数え、3で割る = 1行あたりの単語数。完全なページの行数を数える = 1ページあたりの行数。 3. 1ページあたりの単語数 = 1行あたりの単語数 x 1ページあたりの行数。1分あたりの単語数 = 読んだページ数 x 1ページあたりの単語数 / 10。 または、数ページの時間を計測し、1分あたりの単語数の代わりに `"pages_per_hour"` を使用します。 ## 典型的な値 | 読者 / 資料 | 1分あたりの単語数 | |---|---| | 注意深い読書、密度の高いノンフィクション、第二言語 | 150-200 | | 平均的な大人、フィクション | 200-260 | | 速い娯楽読書家 | 300-400 | 斜め読みは読書ではありません: 450以上の速度で計画しないでください。 | 本の種類 | 1ページあたりの単語数 | |---|---| | 大衆向けペーパーバック | 300-350 | | トレードペーパーバックフィクション | 250-300 | | 注釈付きハードカバーノンフィクション | 350-450 | | 大活字、児童向けチャプターブック | 150-200 | | 電子書籍 | 単語数(ストアのページ数やそれを表示するツール)とデフォルトの1ページあたり275語を使用 | ## 難易度係数 `"difficulty"` は読書時間を掛け算します: 1.0 簡単なフィクション、1.2-1.4 密度の高いノンフィクション、長い文の古典、図解のある技術書、1.5以上 ノートを取る教科書、詩、第二言語での読書。 ## ページ数と単語数 - 読者が持っている版のページ数を使用します。同じ本の版によって30%異なることがあります。 - `"pages"` と `"words"` の両方がある場合、プランナーはその版の1ページあたりの単語数を導き出します(ミドルマーチ 316,000 / 880 = 359)。 - 単語数のみの場合、ページ数は推定値です(単語数 / 1ページあたりの単語数)。計画でその旨を伝えます。 ## オーディオブック - `"audio_minutes"` は再生時間、`"speed"` は再生速度: 必要な時間 = 再生時間 / 速度(400分を1.25倍速で = 320分)。 - オーディオは通勤、家事、散歩に適しています: それらの分数が追加なのか、日次の分数にすでに含まれているのかを伝えます。 - 位置はオーディオのh:mmで表示されます。 FILE:references/pacing-and-catch-up.md # ペーシングと追い上げ ## プランナーのスケジュール方法 - 最も早い締め切りを優先: 完了予定日(締め切りからバッファ日を引いたもの)が最も近い本が最初に読まれます。締め切りのない本は最後になります。 - 均等モード(デフォルト): 最も差し迫った締め切りまでの各期間について、時間内に終了する最小の均等な日次分数の割り当てを使用します。余裕がある場合、30分の日が20分の日になることがあります。その締め切りの後、次の期間が再度計算されます。 - ASAPモード: 毎日すべての分数を使用します。本は可能な限り早く終了します。 - フルペースでも締め切りに間に合わない場合、その前の毎日はフルペースで計画され、本はRP01としてフラグが立てられます。 - 日次の停止位置は、`chapter_snap_pages`(デフォルト8)以内に章の終わりがあり、その日のページ数の4分の1以内にある場合、章の終わりに移動します。 ## バッファと追い上げ日 - バッファ日(`buffer_days`、デフォルト1)は、計画された終了と会合の間に置かれます: ノートを読み返したり、悪い週を吸収する時間。 - 追い上げ曜日(`catch_up_days`、例: 日曜日)には計画された読書はありません。遅れた場合のみ使用します。順調な場合は自由時間です。 - 例外(旅行、遅番、静かな土曜日)は、その日の曜日分数を上書きします。 ## 遅れた場合 1. 1日逃した場合: その日のページを次の追い上げ日に読むか、次の3日間のそれぞれに3分の1ずつ追加します。 2. 2日以上遅れた場合: 古い計画を追いかける代わりに、`--from <今日> --at "<Title>=<現在のページ>"` で今日から再計画します。 3. それでも時間内に終わらない場合(RP01): 1つ選びます - 長い本を今すぐ並行して開始する、数日に10〜20分追加する、通勤にオーディオブックを使用する、マークされたセクション(序文、付録、注釈)を斜め読みする、または途中までで到着することを決めてグループに伝える。 ## 計画がきつすぎる兆候 - RP04: ある期間に時間の90%以上を使用。病気や遅い週で崩れます。 - RP02: 本はバッファ日を使用することでのみ収まります。 - 「2セッション」とマークされた日が多い(RP03): 朝と夕方に意図的に読書を分散することを検討します。 ## ブッククラブのヒント - グループが本全体でなく特定のページや章を議論する場合は、それを本の `pages` として使用します。 - 長い古典の場合、クラブが前半を対象にしているかどうかを尋ね、2つの締め切りを計画します。 - 計画の週次チェックポイントを本に付箋で貼っておきます。 FILE:templates/reading-plan.md # 読書計画: <名前>(<開始日> から <最後の締め切り>) **ペース:** <wpm> 単語/分、30分のセッションで約 <ページ数> ページ **時間:** <曜日ごとの分数>; 追い上げ日: <曜日>; バッファ: 各会合の前に <数> 日 **判定:** <すべての締め切りを達成 | BOOK は DATE までに終えられない - オプションを参照> ## 本 | 本 | 残りページ | 必要な時間 | 会合 / 締め切り | 完了予定日 | 計画終了日 | |---|---|---|---|---|---| | <タイトル> | <pp> | <h:mm> | <日付 - イベント> | <日付> | <日付> | ## 週ごと ### <月曜日の日付> の週 - 月: <本 p. a-b> - 火: <...> - 日: 追い上げ / 自由 **日曜夜のチェックポイント:** <本 p. X> ## 締め切りが収まらない場合 - 不足: <日付> までに <本> の <ページ数> ページ - オプション: <読書日に +N 分 | 今すぐ並行して開始 | 通勤にオーディオブック | 章 x-y を斜め読み | 途中までで到着> - 選択: <...> ## 遅れた場合 - 1日逃した? <追い上げ日> を使用するか、次の3日間に <数> ページずつ追加します。 - 2日以上遅れた場合: 今日から再計画します(`--from <日付> --at "<タイトル>=<ページ>"`)。 FILE:examples/book-club.json { "name": "読書計画: 2026年11月〜12月(2つのブッククラブと仕事用の本)", "start": "2026-11-03", "reader": {"words_per_minute": 240, "words_per_page": 280}, "minutes": {"mon": 30, "tue": 30, "wed": 20, "thu": 30, "fri": 25, "sat": 60, "sun": 45}, "catch_up_days": ["sun"], "buffer_days": 2, "max_session_minutes": 60, "chapter_snap_pages": 8, "exceptions": [ {"dates": "2026-11-13..2026-11-15", "minutes": 0, "note": "週末旅行"}, {"dates": "2026-11-26", "minutes": 15, "note": "遅番"}, {"dates": "2026-12-05", "minutes": 120, "note": "静かな土曜日"} ], "books": [ {"title": "日の名残り", "pages": 245, "words_per_page": 265, "deadline": "2026-11-19", "event": "木曜ブッククラブ", "chapters": [1, 9, 45, 101, 125, 165, 212, 239]}, {"title": "システム思考", "pages": 218, "start_page": 23, "difficulty": 1.3, "deadline": "2026-12-01", "event": "チーム読書グループ"}, {"title": "ミドルマーチ", "words": 316000, "pages": 880, "deadline": "2026-12-17", "event": "クラシッククラブ"}, {"title": "ピラネージ(オーディオブック)", "audio_minutes": 400, "speed": 1.25} ] } FILE:examples/book-club-plan.md # 読書計画: 2026年11月〜12月(2つのブッククラブと仕事用の本) 開始 2026-11-03、均等モード、各締め切りの前にバッファ2日、追い上げ日: 日。計画された読書: 81日間で36時間44分。 **判定: 1冊が締め切りまでに終えられない - 計画を変更してください** ## 本 | 本 | 残り | 推定時間 | 締め切り | 完了予定日 | 計画終了日 | 余裕日数 | 1日あたりの平均 | |---|---|---|---|---|---|---|---| | 日の名残り | 245 pp | 4:31 h | 2026-11-19(木曜ブッククラブ) | 2026-11-17 | 2026-11-12 | 7 | 27 pp | | システム思考 | 196 pp | 4:57 h | 2026-12-01(チーム読書グループ) | 2026-11-29 | 2026-11-27 | 4 | 16 pp | | ミドルマーチ | 880 pp | 21:57 h | 2026-12-17(クラシッククラブ) | 2026-12-15 | 2027-01-11 | -25 | 23 pp | | ピラネージ(オーディオブック) | 6:40 オーディオ | 5:20 h | - | - | 2027-01-22 | - | 0:36 | ## 調査結果 - **高 RP01** ミドルマーチは2026-12-17(クラシッククラブ)までに終えられません: 現在のペースでは約452ページ不足。完了予定は2027-01-11。オプション: それまでの42の読書日のそれぞれに+17分、より速く読む、約452ページを斜め読みまたはスキップ、またはより早く開始する - **低 RP03** 60分以上を計画している日が1日(例: 2026-12-05: 120分)- 2つのセッションに分割 - **情報 RP06** ピラネージ(オーディオブック)はオーディオブックです: 計画は1.25倍速でのリスニング時間をカウントします。通勤や家事でカバーできます ## 日ごと | 日付 | 曜日 | 分数 | 読書 | メモ | |---|---|---|---|---| | 2026-11-03 | 火 | 30 | 日の名残り p. 1-27(27 pp) | | | 2026-11-04 | 水 | 19 | 日の名残り p. 28-44(17 pp) | | | 2026-11-05 | 木 | 30 | 日の名残り p. 45-71(27 pp) | | | 2026-11-06 | 金 | 25 | 日の名残り p. 72-94(23 pp) | | | 2026-11-07 | 土 | 60 | 日の名残り p. 95-148(54 pp) | | | 2026-11-08 | 日 | - | - | 追い上げ / 予備(45分利用可能) | | 2026-11-09 | 月 | 30 | 日の名残り p. 149-175(27 pp) | | | 2026-11-10 | 火 | 30 | 日の名残り p. 176-202(27 pp) | | | 2026-11-11 | 水 | 20 | 日の名残り p. 203-221(19 pp) | | | 2026-11-12 | 木 | 30 | 日の名残り p. 222-245(24 pp); システム思考 p. 23-24(2 pp) | | | 2026-11-13 | 金 | - | - | 週末旅行 | | 2026-11-14 | 土 | - | - | 週末旅行 | | 2026-11-15 | 日 | - | - | 週末旅行 | | 2026-11-16 | 月 | 30 | システム思考 p. 25-44(20 pp) | | | 2026-11-17 | 火 | 30 | システム思考 p. 45-64(20 pp) | 日の名残り 完了予定日 | | 2026-11-18 | 水 | 20 | システム思考 p. 65-77(13 pp) | | | 2026-11-19 | 木 | 30 | システム思考 p. 78-97(20 pp) | 締め切り 日の名残り - 木曜ブッククラブ | | 2026-11-20 | 金 | 25 | システム思考 p. 98-113(16 pp) | | | 2026-11-21 | 土 | 60 | システム思考 p. 114-153(40 pp) | | | 2026-11-22 | 日 | - | - | 追い上げ / 予備(45分利用可能) | | 2026-11-23 | 月 | 30 | システム思考 p. 154-172(19 pp) | | | 2026-11-24 | 火 | 30 | システム思考 p. 173-192(20 pp) | | | 2026-11-25 | 水 | 20 | システム思考 p. 193-205(13 pp) | | | 2026-11-26 | 木 | 15 | システム思考 p. 206-215(10 pp) | 遅番 | | 2026-11-27 | 金 | 25 | システム思考 p. 216-218(3 pp); ミドルマーチ p. 1-14(14 pp) | | | 2026-11-28 | 土 | 60 | ミドルマーチ p. 15-54(40 pp) | | | 2026-11-29 | 日 | - | - | 追い上げ / 予備(45分利用可能); システム思考 完了予定日 | | 2026-11-30 | 月 | 30 | ミドルマーチ p. 55-74(20 pp) | | | 2026-12-01 | 火 | 30 | ミドルマーチ p. 75-94(20 pp) | 締め切り システム思考 - チーム読書グループ | | 2026-12-02 | 水 | 20 | ミドルマーチ p. 95-107(13 pp) | | | 2026-12-03 | 木 | 30 | ミドルマーチ p. 108-128(21 pp) | | | 2026-12-04 | 金 | 25 | ミドルマーチ p. 129-144(16 pp) | | | 2026-12-05 | 土 | 120 | ミドルマーチ p. 145-224(80 pp) | 静かな土曜日; 2セッション | | 2026-12-06 | 日 | - | - | 追い上げ / 予備(45分利用可能) | | 2026-12-07 | 月 | 30 | ミドルマーチ p. 225-244(20 pp) | | | 2026-12-08 | 火 | 30 | ミドルマーチ p. 245-265(21 pp) | | | 2026-12-09 | 水 | 20 | ミドルマーチ p. 266-278(13 pp) | | | 2026-12-10 | 木 | 30 | ミドルマーチ p. 279-298(20 pp) | | | 2026-12-11 | 金 | 25 | ミドルマーチ p. 299-315(17 pp) | | | 2026-12-12 | 土 | 60 | ミドルマーチ p. 316-355(40 pp) | | | 2026-12-13 | 日 | - | - | 追い上げ / 予備(45分利用可能) | | 2026-12-14 | 月 | 30 | ミドルマーチ p. 356-375(20 pp) | | | 2026-12-15 | 火 | 30 | ミドルマーチ p. 376-395(20 pp) | ミドルマーチ 完了予定日 | | 2026-12-16 | 水 | 20 | ミドルマーチ p. 396-408(13 pp) | | | 2026-12-17 | 木 | 30 | ミドルマーチ p. 409-428(20 pp) | 締め切り ミドルマーチ - クラシッククラブ | | 2026-12-18 | 金 | 25 | ミドルマーチ p. 429-445(17 pp) | | | 2026-12-19 | 土 | 60 | ミドルマーチ p. 446-485(40 pp) | | | 2026-12-20 | 日 | - | - | 追い上げ / 予備(45分利用可能) | | 2026-12-21 | 月 | 30 | ミドルマーチ p. 486-505(20 pp) | | | 2026-12-22 | 火 | 30 | ミドルマーチ p. 506-525(20 pp) | | | 2026-12-23 | 水 | 20 | ミドルマーチ p. 526-539(14 pp) | | | 2026-12-24 | 木 | 30 | ミドルマーチ p. 540-559(20 pp) | | | 2026-12-25 | 金 | 25 | ミドルマーチ p. 560-575(16 pp) | | | 2026-12-26 | 土 | 60 | ミドルマーチ p. 576-615(40 pp) | | | 2026-12-27 | 日 | - | - | 追い上げ / 予備(45分利用可能) | | 2026-12-28 | 月 | 30 | ミドルマーチ p. 616-635(20 pp) | | | 2026-12-29 | 火 | 30 | ミドルマーチ p. 636-656(21 pp) | | | 2026-12-30 | 水 | 20 | ミドルマーチ p. 657-669(13 pp) | | | 2026-12-31 | 木 | 30 | ミドルマーチ p. 670-689(20 pp) | | | 2027-01-01 | 金 | 25 | ミドルマーチ p. 690-706(17 pp) | | | 2027-01-02 | 土 | 60 | ミドルマーチ p. 707-746(40 pp) | | | 2027-01-03 | 日 | - | - | 追い上げ / 予備(45分利用可能) | | 2027-01-04 | 月 | 30 | ミドルマーチ p. 747-766(20 pp) | | | 2027-01-05 | 火 | 30 | ミドルマーチ p. 767-786(20 pp) | | | 2027-01-06 | 水 | 20 | ミドルマーチ p. 787-799(13 pp) | | | 2027-01-07 | 木 | 30 | ミドルマーチ p. 800-819(20 pp) | | | 2027-01-08 | 金 | 25 | ミドルマーチ p. 820-836(17 pp) | | | 2027-01-09 | 土 | 60 | ミドルマーチ p. 837-876(40 pp) | | | 2027-01-10 | 日 | - | - | 追い上げ / 予備(45分利用可能) | | 2027-01-11 | 月 | 30 | ミドルマーチ p. 877-880(4 pp); ピラネージ(オーディオブック) 0:00-0:30 | | | 2027-01-12 | 火 | 30 | ピラネージ(オーディオブック) 0:30-1:08 | | | 2027-01-13 | 水 | 20 | ピラネージ(オーディオブック) 1:08-1:33 | | | 2027-01-14 | 木 | 30 | ピラネージ(オーディオブック) 1:33-2:10 | | | 2027-01-15 | 金 | 25 | ピラネージ(オーディオブック) 2:10-2:41 | | | 2027-01-16 | 土 | 60 | ピラネージ(オーディオブック) 2:41-3:56 | | | 2027-01-17 | 日 | - | - | 追い上げ / 予備(45分利用可能) | | 2027-01-18 | 月 | 30 | ピラネージ(オーディオブック) 3:56-4:34 | | | 2027-01-19 | 火 | 30 | ピラネージ(オーディオブック) 4:34-5:11 | | | 2027-01-20 | 水 | 20 | ピラネージ(オーディオブック) 5:11-5:36 | | | 2027-01-21 | 木 | 30 | ピラネージ(オーディオブック) 5:36-6:14 | | | 2027-01-22 | 金 | 21 | ピラネージ(オーディオブック) 6:14-6:40 | | ## 週次チェックポイント(日曜の夜までにどこにいるべきか) - 2026-11-08: 日の名残り p. 148 - 2026-11-15: システム思考 p. 24 - 2026-11-22: システム思考 p. 153 - 2026-11-29: ミドルマーチ p. 54 - 2026-12-06: ミドルマーチ p. 224 - 2026-12-13: ミドルマーチ p. 355 - 2026-12-20: ミドルマーチ p. 485 - 2026-12-27: ミドルマーチ p. 615 - 2027-01-03: ミドルマーチ p. 746 - 2027-01-10: ミドルマーチ p. 876 - 2027-01-17: ピラネージ(オーディオブック) 3:56 ## 遅れた場合 - 日の名残り: 1日逃すと約27ページの損失。予備日 2026-11-08 で取り戻すか、次の3つの読書日のそれぞれに10ページ追加します。いつでも `--from <日付> --at "日の名残り=<ページ>"` で再計画できます。 - システム思考: 1日逃すと約16ページの損失。予備日 2026-11-22、2026-11-29 で取り戻すか、次の3つの読書日のそれぞれに6ページ追加します。いつでも `--from <日付> --at "システム思考=<ページ>"` で再計画できます。 - ミドルマーチ: 1日逃すと約23ページの損失。予備日 2026-11-29、2026-12-06、2026-12-13 で取り戻すか、次の3つの読書日のそれぞれに8ページ追加します。いつでも `--from <日付> --at "ミドルマーチ=<ページ>"` で再計画できます。 FILE:scripts/reading_plan.py #!/usr/bin/env python3 """読書計画とページペースプランナー(標準ライブラリのみ、Python 3.8+)。 本のリスト(ページ数、単語数、またはオーディオブックの長さ)、読書速度、毎日の利用可能な分数を、すべての締め切り(ブッククラブ、コース、試験)を満たすページ範囲付きの日次計画に変換し、各締め切りの前にバッファ日と週の追い上げ日を確保し、負荷を均等に分散し、遅れた場合の対処法を伝えます。 使用法: reading_plan.py PLAN.json [--mode even|asap] [--from YYYY-MM-DD] [--at "Title=page"]... [--json] reading_plan.py --self-test 終了コード: 0 = すべての締め切りを達成、1 = 少なくとも1冊が締め切りまでに終えられない、2 = 不正な入力。 """ import argparse, datetime, json, math, re, sys DAYS = ["mon", "tue", "wed", "thu", "fri", "sat", "sun"] SEV = {"high": 0, "medium": 1, "low": 2, "info": 3} class InputError(Exception): pass def pdate(s, what): try: return datetime.date.fromisoformat(str(s)) except ValueError: raise InputError("%s は YYYY-MM-DD 形式である必要があります。入力値: %r" % (what, s)) def expand_dates(spec): spec = str(spec) if ".." in spec: a, b = spec.split("..", 1) a, b = pdate(a.strip(), "日付範囲"), pdate(b.strip(), "日付範囲") if b < a: raise InputError("日付範囲 %s は開始前に終了しています" % spec) return [a + datetime.timedelta(days=i) for i in range((b - a).days + 1)] return [pdate(spec.strip(), "日付")] # ---------------------------------------------------------------- モデル def load(cfg, start_override=None, at=None): if not isinstance(cfg, dict) or not isinstance(cfg.get("books"), list) or not cfg["books"]: raise InputError("計画には空でない 'books' リストが必要です") reader = cfg.get("reader") or {} wpm = float(reader.get("words_per_minute", 230)) wpp_default = float(reader.get("words_per_page", 275)) pph = reader.get("pages_per_hour") if wpm <= 0 or wpp_default <= 0: raise InputError("words_per_minute と words_per_page は正の値である必要があります") start = pdate(start_override or cfg.get("start") or datetime.date.today().isoformat(), "開始日") mins = cfg.get("minutes") if isinstance(mins, (int, float)): mins = {d: mins for d in DAYS} if not isinstance(mins, dict): raise InputError("'minutes' は数値または {mon: .., tue: ..} 形式である必要があります") mins = {k.lower()[:3]: float(v) for k, v in mins.items()} bad = set(mins) - set(DAYS) if bad: raise InputError("minutes に不明な曜日キーがあります: %s" % ", ".join(sorted(bad))) catch = {d.lower()[:3] for d in cfg.get("catch_up_days", [])} exc = {} for e in cfg.get("exceptions", []): for d in expand_dates(e.get("dates") or e.get("date")): exc[d] = (float(e.get("minutes", 0)), e.get("note", "")) books = [] at = at or {} for i, b in enumerate(cfg["books"], 1): t = b.get("title") or "本 %d" % i if "audio_minutes" in b: speed = float(b.get("speed", 1.0)) total = float(b["audio_minutes"]) unit_min = 1.0 / speed # オーディオ1分あたりのあなたの時間(分) units, kind = total, "audio" done = float(at.get(t, b.get("start_minute", 0))) else: wpp = float(b.get("words_per_page") or (float(b["words"]) / float(b["pages"]) if b.get("words") and b.get("pages") else wpp_default)) pages = float(b["pages"]) if b.get("pages") else (float(b["words"]) / wpp if b.get("words") else None) if not pages or pages <= 0: raise InputError("本 %r には pages、words、または audio_minutes が必要です" % t) diff = float(b.get("difficulty", 1.0)) if pph and not b.get("words"): unit_min = 60.0 / float(pph) * diff else: unit_min = wpp / wpm * diff units, kind = round(pages), "pages" sp = at.get(t, b.get("start_page", 1)) done = max(0.0, float(sp) - 1) dl = pdate(b["deadline"], "%r の締め切り" % t) if b.get("deadline") else None chapters = sorted(int(c) for c in b.get("chapters", [])) books.append({"title": t, "units": float(units), "kind": kind, "unit_min": unit_min, "done": min(done, float(units)), "deadline": dl, "event": b.get("event", ""), "chapters": chapters, "order": i}) buf = int(cfg.get("buffer_days", 1)) return {"start": start, "mins": mins, "catch": catch, "exc": exc, "books": books, "buffer": buf, "max_session": float(cfg.get("max_session_minutes", 0) or 0), "snap": int(cfg.get("chapter_snap_pages", 8)), "name": cfg.get("name", "読書計画"), "wpm": wpm} def capacity(m, d): """(計画分数、利用可能分数(追い上げ日含む)、メモ)。""" if d in m["exc"]: v, note = m["exc"][d] return v, v, note or ("読書なし" if v == 0 else "") wd = DAYS[d.weekday()] v = m["mins"].get(wd, 0.0) if wd in m["catch"]: return 0.0, v, "追い上げ日" return v, v, "" def plan(m, mode="even"): F = [] books = [b for b in m["books"]] for b in books: b["need"] = (b["units"] - b["done"]) * b["unit_min"] if b["need"] <= 0: F.append(("info", "RP05", "%s はすでに読み終えています - 計画から除外" % b["title"])) b["target"] = None if b["deadline"] and b["deadline"] < m["start"]: if b["need"] > 0: F.append(("medium", "RP05", "%s: 締め切り %s はすでに過ぎています - 締め切りなしで計画" % (b["title"], b["deadline"]))) b["deadline"] = None if b["deadline"]: b["target"] = b["deadline"] - datetime.timedelta(days=m["buffer"]) if b["target"] < m["start"]: b["target"] = m["start"] F.append(("medium", "RP05", "%s: 締め切り %s は %d 日のバッファには近すぎます - 締め切りに合わせて計画" % (b["title"], b["deadline"], m["buffer"]))) todo = [b for b in books if b["need"] > 0] dated = sorted([b for b in todo if b["target"]], key=lambda b: (b["target"], b["order"])) undated = [b for b in todo if not b["target"]] seq = dated + undated last = max([b["deadline"] for b in dated] or [m["start"]]) # 地平線: フルキャパシティで全てを終えるのに十分な日数 + 余裕 weekly = sum(m["mins"].get(d, 0) for d in DAYS if d not in m["catch"]) or 1 total_need = sum(b["need"] for b in seq) horizon_end = max(last, m["start"]) + datetime.timedelta(days=int(total_need / weekly * 7) + 60) days, d = [], m["start"] while d <= horizon_end: p, a, note = capacity(m, d) days.append({"date": d, "cap": p, "avail": a, "note": note, "planned": 0.0, "items": []}) d += datetime.timedelta(days=1) idx = {x["date"]: i for i, x in enumerate(days)} # セグメントごとの負荷係数(均等モード): すべてのターゲットを満たす最小の均一なキャパシティの割合 load = [1.0] * len(days) if mode == "even" and dated: seg_start, k0 = 0, 0 while k0 < len(dated): best, bk = 0.0, k0 cum = 0.0 for k in range(k0, len(dated)): cum += dated[k]["need"] ti = idx[dated[k]["target"]] cap = sum(x["cap"] for x in days[seg_start:ti + 1]) r = cum / cap if cap > 0 else float("inf") if r >= best: best, bk = r, k L = min(best * 1.03, 1.0) # 章末停止と丸めのための3%のヘッドルーム end = idx[dated[bk]["target"]] for i in range(seg_start, end + 1): load[i] = L seg_start, k0 = end + 1, bk + 1 # 最後の締め切りの後: 締め切りのない本は、これまでの計画の平均負荷(少なくとも0.5) tail = max(0.5, max(load[:seg_start] or [1.0])) for i in range(seg_start, len(days)): load[i] = tail if undated else 1.0 # 割り当て(最も早い締め切りを優先)、各本の位置を追跡 qi = 0 pos = {b["title"]: b["done"] for b in seq} for i, day in enumerate(days): budget = day["cap"] * load[i] while budget > 1e-9 and qi < len(seq): b = seq[qi] remaining = (b["units"] - pos[b["title"]]) * b["unit_min"] use = min(budget, remaining) before = pos[b["title"]] after = before + use / b["unit_min"] if after > b["units"] - 1e-6: after = b["units"] elif b["kind"] == "pages": snapped = snap(after, b, m["snap"], use / b["unit_min"]) if snapped != after: after = snapped use = (after - before) * b["unit_min"] budget = use # 章の終わりで停止: その日はここで終了 pos[b["title"]] = after day["items"].append({"title": b["title"], "from": before, "to": after, "kind": b["kind"], "minutes": use}) day["planned"] += use budget -= use if after >= b["units"] - 1e-6: b["finish"] = day["date"]; qi += 1 elif budget <= 1e-9 or use <= 1e-9: break if qi >= len(seq): break days = [x for x in days if x["date"] <= max([b.get("finish", m["start"]) for b in seq] + [last])] for b in seq: b.setdefault("finish", None) # 調査結果 for b in dated: f = b["finish"] if f is None or f > b["deadline"]: avail = [x for x in days if x["date"] <= b["deadline"]] share = sum(it["minutes"] for x in avail for it in x["items"] if it["title"] == b["title"]) short_min = b["need"] - share # 締め切りまでに利用可能な追い上げ/バッファ時間もカウント reading_days = sum(1 for x in avail if x["avail"] > 0) or 1 short_units = short_min / b["unit_min"] msg = "%s は %s%s までに終えられません: 現在のペースでは約 %d %s 不足。完了予定は %s。オプション: それまでの %d の読書日のそれぞれに +%d 分、より速く読む、約 %d %s を斜め読みまたはスキップ、またはより早く開始する" % ( b["title"], b["deadline"], " (%s)" % b["event"] if b["event"] else "", math.ceil(short_units), "ページ" if b["kind"] == "pages" else "オーディオ分数", f or "計画の範囲外", math.ceil(short_min / reading_days), reading_days, math.ceil(short_units), "ページ" if b["kind"] == "pages" else "オーディオ分数") F.append(("high", "RP01", msg)) elif f > b["target"]: F.append(("medium", "RP02", "%s は %s に終了し、%s の前の %d バッファ日以内です - 1日逃す余裕はありません" % (b["title"], f, b["deadline"], m["buffer"]))) if mode == "even": peak = max(load) if load else 0 if 0.9 <= peak <= 1.0 and not any(f[1] == "RP01" for f in F): F.append(("medium", "RP04", "計画は最も忙しい期間に計画された読書時間の %d%% を使用します - 1週間の悪い週でバッファに追いやられます" % round(peak * 100))) if m["max_session"]: long = [x for x in days if x["planned"] > m["max_session"] + 0.5] if long: F.append(("low", "RP03", "%d 日が %d 分以上を計画しています(例: %s: %d 分)- 2つのセッションに分割" % ( len(long), m["max_session"], long[0]["date"], round(long[0]["planned"])))) for b in seq: if b["kind"] == "audio": F.append(("info", "RP06", "%s はオーディオブックです: 計画は %s 倍速でのリスニング時間をカウントします。通勤や家事でカバーできます" % (b["title"], ("%.2f" % (1 / b["unit_min"])).rstrip("0").rstrip(".")))) F.sort(key=lambda f: (SEV[f[0]], f[1])) return {"days": days, "books": seq, "all_books": books, "findings": F, "load": load} def snap(after, b, snap_pages, day_units): """1日の停止位置を、近くにある場合(snap_pages 以内かつその日の25%以内)章の終わりに移動します。""" if not b["chapters"] or snap_pages <= 0: return after lim = min(snap_pages, max(1.0, day_units * 0.25)) ends = [c - 1 for c in b["chapters"] if c - 1 > 0] best = None for e in ends: if abs(e - after) <= lim and e > after - day_units + 0.5 and (best is None or abs(e - after) < abs(best - after)): best = e return float(best) if best is not None else after # ---------------------------------------------------------------- レンダリング def fmt_item(it): if it["kind"] == "audio": return "%s %s-%s" % (it["title"], hm(it["from"]), hm(it["to"])) a = int(round(it["from"])) + 1 z = int(round(it["to"])) if z < a: z = a return "%s p. %d-%d (%d pp)" % (it["title"], a, z, z - a + 1) def hm(x): x = int(round(x)) return "%d:%02d" % (x // 60, x % 60) def checkpoints(r): out = [] pos = {} for x in r["days"]: for it in x["items"]: pos[it["title"]] = it if x["date"].weekday() == 6 and pos: # 日曜日 out.append((x["date"], "; ".join("%s %s" % (t, ("p. %d" % round(it["to"]) if it["kind"] == "pages" else hm(it["to"]))) for t, it in pos.items() if it["to"] < next(b["units"] for b in r["books"] if b["title"] == t) - 1e-6) or "現在の本はすべて完了")) return out def report(r, m, mode): hi = [f for f in r["findings"] if f[0] == "high"] out = ["# %s" % m["name"], ""] tot_min = sum(x["planned"] for x in r["days"]) out.append("開始 %s、%s モード、各締め切りの前にバッファ %d 日、追い上げ日: %s。計画された読書: %d 時間 %02d 分(%d 日間)。" % ( m["start"], mode, m["buffer"], ", ".join(sorted(m["catch"], key=DAYS.index)) or "なし", tot_min // 60, tot_min % 60, len(r["days"]))) out += ["", "**判定: %s**" % ("%d 冊が締め切りまでに終えられない - 計画を変更してください" % len(hi) if hi else "すべての締め切りを達成"), ""] out += ["## 本", "", "| 本 | 残り | 推定時間 | 締め切り | 完了予定日 | 計画終了日 | 余裕日数 | 1日あたりの平均 |", "|---|---|---|---|---|---|---|---|"] for b in r["all_books"]: left = b["units"] - b["done"] rd = [x for x in r["days"] if any(it["title"] == b["title"] for it in x["items"])] avg = left / len(rd) if rd else 0 spare = (b["deadline"] - b["finish"]).days if b.get("finish") and b["deadline"] else None out.append("| %s | %s | %s | %s | %s | %s | %s | %s |" % ( b["title"], ("%d pp" % round(left)) if b["kind"] == "pages" else hm(left) + " オーディオ", hm(b["need"]) + " 時間", "%s %s" % (b["deadline"], "(%s)" % b["event"] if b["event"] else "") if b["deadline"] else "-", b["target"] or "-", b.get("finish") or ("完了" if b["need"] <= 0 else "時間内に終わらない"), "-" if spare is None else spare, ("%.0f pp" % avg) if b["kind"] == "pages" else hm(avg))) out += ["", "## 調査結果", ""] out += ["- **%s %s** %s" % (s.upper(), c, msg) for s, c, msg in r["findings"]] or ["- なし"] out += ["", "## 日ごと", "", "| 日付 | 曜日 | 分数 | 読書 | メモ |", "|---|---|---|---|---|"] events = {} for b in r["all_books"]: if b["deadline"]: events.setdefault(b["deadline"], []).append("締め切り %s%s" % (b["title"], " - " + b["event"] if b["event"] else "")) if b["target"] and b["target"] != b["deadline"]: events.setdefault(b["target"], []).append("%s 完了予定日" % b["title"]) for x in r["days"]: note = [x["note"]] if x["note"] else [] if x["note"] == "追い上げ日" or (x["planned"] < 0.5 and x["avail"] > 0): note = ["追い上げ / 予備(%d 分利用可能)" % x["avail"]] if x["avail"] > 0 else note note += events.get(x["date"], []) if m["max_session"] and x["planned"] > m["max_session"] + 0.5: note.append("2セッション") out.append("| %s | %s | %s | %s | %s |" % (x["date"], x["date"].strftime("%a"), round(x["planned"]) if x["planned"] >= 0.5 else "-", "; ".join(fmt_item(it) for it in x["items"]) or "-", "; ".join(note))) cps = checkpoints(r) if cps: out += ["", "## 週次チェックポイント(日曜の夜までにどこにいるべきか)", ""] out += ["- %s: %s" % (d, s) for d, s in cps] out += ["", "## 遅れた場合", ""] for b in r["books"]: if b["kind"] != "pages" or not b["deadline"]: continue rd = [x for x in r["days"] if any(it["title"] == b["title"] for it in x["items"])] if not rd: continue day_pp = sum(it["to"] - it["from"] for x in rd for it in x["items"] if it["title"] == b["title"]) / len(rd) spare = [x for x in r["days"] if x["date"] <= b["deadline"] and x["date"] >= rd[0]["date"] and x["avail"] > 0 and x["planned"] < 0.5] out.append("- %s: 1日逃すと約 %d ページの損失。%s で取り戻すか、次の3つの読書日のそれぞれに %d ページ追加します。いつでも `--from <日付> --at \"%s=<ページ>\"` で再計画できます。" % ( b["title"], round(day_pp), ("予備日 " + ", ".join(str(x["date"]) for x in spare[:3])) if spare else "締め切り前の予備日なし", math.ceil(day_pp / 3), b["title"])) out.append("") return "\n".join(out) # ---------------------------------------------------------------- セルフテスト def self_test(): tests = [] def t(name, cond): tests.append((name, bool(cond))) def run(cfg, mode="even", **kw): m = load(cfg, **kw) return plan(m, mode), m base = {"start": "2026-11-02", "reader": {"words_per_minute": 250, "words_per_page": 250}, "minutes": 30, "buffer_days": 0} r, m = run(dict(base, books=[{"title": "A", "pages": 60, "deadline": "2026-11-08"}]), "asap") t("asap: 1ページ/分で30ページ/日、2日目に終了", r["books"][0]["finish"] == datetime.date(2026, 11, 3)) r, m = run(dict(base, books=[{"title": "A", "pages": 70, "deadline": "2026-11-08"}])) t("even: 週に分散(約10分/日 + 3%ヘッドルーム)", r["books"][0]["finish"] >= datetime.date(2026, 11, 7) and 10 <= r["days"][0]["planned"] <= 10.31) r, m = run(dict(base, books=[{"title": "A", "pages": 300, "deadline": "2026-11-08"}])) t("実行不可能 -> RP01 高", any(f[1] == "RP01" and f[0] == "high" for f in r["findings"])) t("RP01 は追加分数を提案", any("+13分" in f[2] for f in r["findings"] if f[1] == "RP01")) r, m = run(dict(base, buffer_days=2, books=[{"title": "A", "pages": 70, "deadline": "2026-11-08"}])) t("バッファ日: 2日早く終了", r["books"][0]["finish"] == datetime.date(2026, 11, 6)) r, m = run(dict(base, catch_up_days=["sun"], books=[{"title": "A", "pages": 60, "deadline": "2026-11-08"}])) t("追い上げ日曜日には計画された読書なし", r["days"][-1]["planned"] == 0 and r["books"][0]["finish"] == datetime.date(2026, 11, 7)) r, m = run(dict(base, exceptions=[{"dates": "2026-11-03..2026-11-04", "minutes": 0, "note": "旅行"}], books=[{"title": "A", "pages": 50, "deadline": "2026-11-08"}])) t("例外範囲には読書なし", all(x["planned"] == 0 for x in r["days"][1:3])) two = [{"title": "遅い", "pages": 30, "deadline": "2026-11-20"}, {"title": "早い", "pages": 60, "deadline": "2026-11-04"}] r, m = run(dict(base, books=two)) t("最も早い締め切りを優先", r["days"][0]["items"][0]["title"] == "早い") t("きついセグメントの後は軽い負荷", r["load"][0] > 0.6 and r["load"][5] < 0.2) r, m = run(dict(base, books=[{"title": "A", "pages": 70, "deadline": "2026-11-08"}]), at={"A": 36}) t("--at はページから再開", abs(sum(x["planned"] for x in r["days"]) - 35) < 0.01) r, m = run(dict(base, books=[{"title": "W", "words": 25000, "deadline": "2026-11-08"}])) t("単語数 -> words_per_page でページ数に変換", r["all_books"][0]["units"] == 100) r, m = run(dict(base, books=[{"title": "H", "pages": 70, "difficulty": 1.5, "deadline": "2026-11-08"}])) t("難易度は時間をスケール", abs(r["books"][0]["need"] - 105) < 0.01) r, m = run(dict(base, books=[{"title": "Au", "audio_minutes": 300, "speed": 1.5, "deadline": "2026-11-08"}])) t("オーディオブック 1.5倍速 = 200分", abs(r["books"][0]["need"] - 200) < 0.01 and any(f[1] == "RP06" for f in r["findings"])) r, m = run(dict(base, books=[{"title": "C", "pages": 70, "deadline": "2026-11-08", "chapters": [1, 12, 23, 31, 42, 55]}])) ends = [round(it["to"]) for x in r["days"] for it in x["items"]] t("章スナップは章の終わりで停止", 11 in ends and 22 in ends and ends[-1] == 70) t("1日1冊につき1項目", all(len({it["title"] for it in x["items"]}) == len(x["items"]) for x in r["days"])) t("ページ範囲は重複しない", all(fmt_item(it) for x in r["days"] for it in x["items"]) and [int(round(it["from"])) for x in r["days"] for it in x["items"]][1:] == [int(round(it["to"])) for x in r["days"] for it in x["items"]][:-1]) r, m = run(dict(base, max_session_minutes=20, minutes=60, books=[{"title": "A", "pages": 300, "deadline": "2026-11-08"}])) t("長いセッション -> RP03", any(f[1] == "RP03" for f in r["findings"])) r, m = run(dict(base, books=[{"title": "A", "pages": 200, "deadline": "2026-11-08"}])) t("きつい計画 -> RP04 または RP01", any(f[1] in ("RP04", "RP01") for f in r["findings"])) r, m = run(dict(base, books=[{"title": "A", "pages": 30}, {"title": "B", "pages": 60, "deadline": "2026-11-05"}])) t("締め切りのない本は締め切りのある本の後", r["books"][-1]["title"] == "A" and r["books"][-1]["finish"]) r, m = run(dict(base, books=[{"title": "A", "pages": 20, "start_page": 21}])) t("読み終えた本 -> RP05", any(f[1] == "RP05" for f in r["findings"])) t("pages_per_hour", abs(load(dict(base, reader={"pages_per_hour": 30}, books=[{"title": "P", "pages": 30}]))["books"][0]["unit_min"] - 2) < 1e-9) for bad in [dict(base, books=[]), dict(base, books=[{"title": "x"}]), dict(base, minutes={"funday": 3}, books=[{"title": "a", "pages": 3}]), dict(base, books=[{"title": "a", "pages": 3, "deadline": "2026-13-01"}])]: try: run(bad); okk = False except InputError: okk = True t("不正な入力は拒否: %s" % (list(bad.get("minutes", {})) if isinstance(bad.get("minutes"), dict) else (bad["books"][0] if bad["books"] else "本なし")), okk) r, m = run(dict(base, books=[{"title": "古い", "pages": 30, "deadline": "2026-10-01"}])) t("過ぎた締め切り -> RP05、締め切りなしで計画", any(f[1] == "RP05" for f in r["findings"]) and r["books"][0]["finish"]) rep = report(*run(dict(base, books=two)), "even") t("レポートには判定、日次表、追い上げアドバイスがある", "判定" in rep and "| 日付 |" in rep and "遅れた場合" in rep) for name, okk in tests: print(("PASS " if okk else "FAIL ") + name) n = sum(1 for _, okk in tests if okk) print("%d/%d パス" % (n, len(tests))) return 0 if n == len(tests) else 1 def main(argv=None): ap = argparse.ArgumentParser(description=__doc__.split("\n")[0]) ap.add_argument("plan", nargs="?") ap.add_argument("--mode", choices=["even", "asap"], default="even") ap.add_argument("--from", dest="start", help="この日付から再計画(YYYY-MM-DD)") ap.add_argument("--at", action="append", default=[], help='"Title=page"(オーディオブックの場合は "Title=minute"): 現在の位置') ap.add_argument("--json", action="store_true") ap.add_argument("--self-test", action="store_true") a = ap.parse_args(argv) if a.self_test: return self_test() if not a.plan: ap.error("計画JSONが必要です(または --self-test)") try: try: cfg = json.load(open(a.plan, encoding="utf-8")) except (OSError, ValueError) as e: raise InputError(str(e)) at = {} for s in a.at: if "=" not in s: raise InputError("--at は Title=page 形式である必要があります。入力値: %r" % s) k, v = s.rsplit("=", 1) try: at[k.strip()] = float(v) except ValueError: raise InputError("--at のページは数値である必要があります。入力値: %r" % v) titles = {b.get("title") for b in cfg.get("books", [])} if isinstance(cfg, dict) else set() for k in at: if k not in titles: raise InputError("--at: %r というタイトルの本はありません" % k) m = load(cfg, a.start, at) r = plan(m, a.mode) except (InputError, KeyError, TypeError, ValueError) as e: print("エラー: %s" % e, file=sys.stderr) return 2 if a.json: out = {"books": [{"title": b["title"], "deadline": str(b["deadline"]) if b["deadline"] else None, "finish": str(b.get("finish")) if b.get("finish") else None, "minutes_needed": round(b["need"])} for b in r["all_books"]], "days": [{"date": str(x["date"]), "minutes": round(x["planned"]), "read": [fmt_item(it) for it in x["items"]]} for x in r["days"]], "findings": [{"severity": s, "code": c, "message": msg} for s, c, msg in r["findings"]]} print(json.dumps(out, indent=2)) else: print(report(r, m, a.mode)) return 1 if any(f[0] == "high" for f in r["findings"]) else 0 if __name__ == "__main__": sys.exit(main())

ログインして完全なプロンプトを表示

次で続行:

ログインすると、次に同意したことになります: 利用規約 と プライバシーポリシー

使い方

このプロンプトは productivity 向けに設計されています。上の内容をコピーして、お好みの AI ツールに貼り付けてください。

最良の結果を得るには、プレースホルダー(角括弧や大文字で示された部分)を具体的な要件に置き換えてください。

参考資料

カテゴリ:productivity| prompts.chat| reading-plan| book-club

ノート