Add fetch_evening_tasks.py script and shared notion_helpers module

Refactors weekly-template's Notion client setup into a shared helper
(with .env/dotenv support and full pagination), and adds a dedicated
evening-prep script so both commands can fall back to script-based
fetching instead of the MCP search/fetch pattern.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
aj.siegel
2026-09-21 23:05:57 -04:00
parent 339f9b1034
commit 20d8c1aedc
8 changed files with 229 additions and 59 deletions

View File

@@ -30,12 +30,11 @@ Let me know how much unscheduled time I have.
* Directly beneath the Agenda table, if Step 1b found a workout scheduled tomorrow, add a one-line **Workout** note stating whether the 9 AM block is clear or naming the conflicting meeting.
## Step 3: Get task data
This Notion MCP has no filter/query tool and `view://` URLs aren't fetchable, so use the search → fetch → filter pattern (see CLAUDE.md "HOW TO QUERY THESE DATABASES"). Do NOT try to fetch views and do NOT print any "falling back" message.
1. **Get this week's focus projects** — read the current Weekly Note (`Weekly Notes/Weekly Note - <Sunday's date>.md`) and use its listed focus projects + weekly goals. (Only if that note is missing: `notion-search` the Projects collection `collection://2c0abd6c-450a-807a-a3d7-000b8d8aaecc` with `page_size: 25`, fetch the results in parallel, and keep those with `Focus this week` = `__YES__` and `Status` = "Active".)
2. **Find candidate tasks** — `notion-search` the Tasks collection `collection://2c0abd6c-450a-8085-80a0-000ba136a154` with `page_size: 25`, `max_highlight_length: 0`, and a `query` seeded with the focus-project names plus "blocking due this week suggested today".
3. **Fetch candidate task pages in parallel** to read their real properties (`Status`, `Suggested for today`, `Due date`, `Blocking`, `Priority`, `Project`).
4. **Filter & rank locally**: drop `Status` = "Done" or "Blocked". Rank the rest by (1) `Blocking` populated, (2) `Suggested for today` ≤ tomorrow, (3) soonest `Due date`.
2. **Get candidate tasks** — run `python3 scripts/fetch_evening_tasks.py`. This already excludes `Status` = "Done" or "Blocked" and paginates through the full Tasks database, so no candidate is missed as the tracker grows.
3. **Rank locally**: by (1) `has_blocking` true, (2) `suggested_for_today` ≤ tomorrow, (3) soonest `due_date`.
**If the script errors or is unavailable** (e.g. missing `.env`/`NOTION_TOKEN` on this machine), fall back instead of failing the command: `notion-search` the Tasks collection `collection://2c0abd6c-450a-8085-80a0-000ba136a154` with `page_size: 25`, `max_highlight_length: 0`, seeded with the focus-project names plus "blocking due this week suggested today" (this Notion MCP has no filter/query tool and `view://` URLs aren't fetchable — see CLAUDE.md "HOW TO QUERY THESE DATABASES"). Fetch candidate pages in parallel to read `Status`, `Suggested for today`, `Due date`, `Blocking`, `Priority`, `Project`, then filter/rank as in step 3 above. Do NOT print any "falling back" message for `view://` fetches, but DO tell me the script failed and you're using the fallback.
Format the note with the following sections:
1. Under **Suggested Tasks** list up to 3 tasks from the ranked set above (Blocking-others tasks always come first). For each, show the task name, related project, and a Notion link.
@@ -46,8 +45,8 @@ Format the note with the following sections:
The Tasks Tracker has a `Top Task` checkbox property and a `Top Tasks` view (filtered to `Top Task = true` AND `Status != Done`) for surfacing the current day's Big 3 at a glance. At most 3 tasks may be checked at any time.
1. Present the Suggested Tasks from Step 3 to AJ and let him confirm or swap any of them before finalizing.
2. `notion-search` the Tasks collection (`collection://2c0abd6c-450a-8085-80a0-000ba136a154`) filtered/queried for `Top Task` = true (or check the pages already known from recent notes) to find any tasks still marked from a prior day.
3. Any currently-marked task whose `Status` = "Done" gets `Top Task` set to false immediately — regardless of whether it's in today's agreed set (it can't be, since Step 3.4 already drops Done tasks). Don't wait for a replacement to unmark a completed task.
2. Use `marked_top_tasks` from the Step 3 script output (already every task with `Top Task = true`, any status — no extra search needed). If the Step 3 fallback was used instead, `notion-search` the Tasks collection (`collection://2c0abd6c-450a-8085-80a0-000ba136a154`) filtered/queried for `Top Task` = true.
3. Any currently-marked task whose `status` = "Done" gets `Top Task` set to false immediately — regardless of whether it's in today's agreed set. Don't wait for a replacement to unmark a completed task.
4. For each other currently-marked task that is NOT in today's agreed set, `notion-update-page` to set `Top Task` = false.
5. For each of the (up to 3) agreed tasks, `notion-update-page` to set `Top Task` = true.
6. Never have more than 3 tasks checked at once — unmark old ones before marking new ones.

View File

@@ -11,15 +11,14 @@ Also remove all prior weekly notes that are more than 1 week old.
Please create a new note in the "Weekly Notes" folder.
The title should be "Weekly Note - Month, Day Year" using today's date.
## Step 2: Review Notion data and create the note
This Notion MCP has no filter/query tool and `view://` URLs aren't fetchable, so use the search → fetch → filter pattern (see CLAUDE.md "HOW TO QUERY THESE DATABASES"). Do NOT try to fetch views and do NOT print any "falling back" message.
## Step 2: Fetch Notion data and create the note
Run `python3 scripts/fetch_weekly_data.py` to get `projects`, `tasks`, and `reading` in one JSON payload (this reduces MCP round-trips and inference cost). `projects` is already filtered to active, non-personal projects and includes each one's current `focus_this_week` state. `tasks` is already filtered to non-Done tasks due within 7 days. `reading` is already filtered to "To Read"/"In Progress".
1. **Active projects** — `notion-search` the Projects collection `collection://2c0abd6c-450a-807a-a3d7-000b8d8aaecc` with `page_size: 25`, `max_highlight_length: 0`. Fetch the returned pages **in parallel**, then keep those with `Status` = "Active" and `Type` ≠ "Personal".
2. **This week's tasks** — `notion-search` the Tasks collection `collection://2c0abd6c-450a-8085-80a0-000ba136a154` with `page_size: 25`, `max_highlight_length: 0`, seeding `query` with the active-project names plus "due this week blocking". Fetch the results in parallel and drop anything with `Status` = "Done".
**If the script errors or is unavailable** (e.g. missing `.env`/`NOTION_TOKEN` on this machine), fall back to the search → fetch → filter pattern instead of failing the command (see CLAUDE.md "HOW TO QUERY THESE DATABASES"): `notion-search` the Projects collection `collection://2c0abd6c-450a-807a-a3d7-000b8d8aaecc` and Tasks collection `collection://2c0abd6c-450a-8085-80a0-000ba136a154` (both `page_size: 25`, `max_highlight_length: 0`), fetch results in parallel, and apply the same filters manually. Do NOT try to fetch `view://` URLs and do NOT print any "falling back" message for those — but DO mention to me that the script failed and you're using the fallback.
List all active projects and ask me which 3 I want to prioritize for the upcoming week.
If there isn't a weekly goal for the selected projects, ask me to write one.
Update the Focus this week field to true for the selected projects (and set to false for any previously selected projects not in my new list).
Update the Focus this week field to true for the selected projects (and false for any project where `focus_this_week` was already true but isn't in my new list) via the Notion MCP — the script is read-only and can't make this update.
Then add the following to the note
@@ -43,9 +42,9 @@ Fetch the Runna ical feed (see CLAUDE.md "Data Sources") with `curl -sL <url>`.
Add a **Workouts this week** section: a table with columns Date, Workout, Block (e.g. 9:00–9:50 AM), and Status (Clear, or "Conflict: <meeting name> <time>"). Skip days with no scheduled workout.
## Step 4: Generate my reading list
Using the same search → fetch → filter pattern: `notion-search` the Reading List collection `collection://2c4abd6c-450a-804a-809a-000bc77d02c1` with `page_size: 25`, `max_highlight_length: 0`. Fetch the returned pages in parallel and keep those with `Status` = "To Read" or "In Progress". (Don't fetch `view://` URLs.)
Use the `reading` list already fetched in Step 2 (from `fetch_weekly_data.py`, or its fallback). If the Step 2 fallback was used and didn't cover reading, `notion-search` the Reading List collection `collection://2c4abd6c-450a-804a-809a-000bc77d02c1` with `page_size: 25`, `max_highlight_length: 0`, fetch the returned pages in parallel, and keep those with `Status` = "To Read" or "In Progress". (Don't fetch `view://` URLs.)
3. Under **Suggested Reading** recommend 2 articles that fit the available time blocks:
Recommend 2 articles under **Suggested Reading** that fit the available time blocks:
- Look at the unscheduled time across the week (from Step 3) to determine what reading time is appropriate
- Match articles based on Read time field:
* Short = <5 minutes (fits in small gaps)