diff --git a/scripts/README.md b/scripts/README.md new file mode 100644 index 0000000..e880a23 --- /dev/null +++ b/scripts/README.md @@ -0,0 +1,53 @@ +# Weekly Planning Scripts + +## Setup + +### 1. Install dependencies +```bash +cd /Users/aj.siegel/Projects/FullFocus +pip3 install -r scripts/requirements.txt +``` + +### 2. Get your Notion API Token + +1. Go to https://www.notion.so/my-integrations +2. Click "+ New integration" +3. Name it "Weekly Planning Script" (or whatever you prefer) +4. Select your workspace +5. Click "Submit" +6. Copy the "Internal Integration Token" + +### 3. Share databases with your integration + +For each database (Projects, Tasks Tracker, Reading List): +1. Open the database page in Notion +2. Click "..." menu → "Connections" +3. Add your "Weekly Planning Script" integration + +### 4. Set environment variable + +Add to your `~/.zshrc` or `~/.bash_profile`: +```bash +export NOTION_TOKEN="secret_your_token_here" +``` + +Then reload: `source ~/.zshrc` + +## Usage + +Run the script to fetch weekly planning data: +```bash +python3 scripts/fetch_weekly_data.py +``` + +Or add it to your PATH and run directly: +```bash +./scripts/fetch_weekly_data.py +``` + +This outputs JSON with: +- Projects marked "Focus this week" +- Tasks due within the next 7 days +- Reading list articles (To Read or In Progress) + +The weekly-template skill automatically calls this script to generate your weekly note, reducing AI inference costs. diff --git a/scripts/fetch_weekly_data.py b/scripts/fetch_weekly_data.py new file mode 100755 index 0000000..027392e --- /dev/null +++ b/scripts/fetch_weekly_data.py @@ -0,0 +1,185 @@ +#!/usr/bin/env python3 +""" +Fetch weekly planning data from Notion databases. +Returns JSON with projects, tasks, and reading suggestions. +""" + +import json +import os +import sys +from datetime import datetime, timedelta +from notion_client import Client + +# Initialize Notion client - will use NOTION_TOKEN env var +notion_token = os.getenv('NOTION_TOKEN') +if not notion_token: + print(json.dumps({"error": "NOTION_TOKEN environment variable not set. See scripts/README.md for setup instructions."}), file=sys.stderr) + sys.exit(1) + +notion = Client(auth=notion_token) + +# Database IDs +PROJECTS_DB = "2c0abd6c450a8090aca3e0b2b0373c17" +TASKS_DB = "2c0abd6c450a805098d3cc0e7d3dfccf" +READING_LIST_DB = "2c4abd6c450a80cbae55c440dd9e2427" + +def get_focus_projects(): + """Get projects with 'Focus this week' = true and Status = Active.""" + results = notion.databases.query( + database_id=PROJECTS_DB, + filter={ + "and": [ + { + "property": "Focus this week", + "checkbox": { + "equals": True + } + }, + { + "property": "Status", + "select": { + "equals": "Active" + } + } + ] + } + ) + + projects = [] + for page in results["results"]: + props = page["properties"] + projects.append({ + "id": page["id"], + "url": page["url"], + "name": props["Name"]["title"][0]["plain_text"] if props["Name"]["title"] else "", + "weekly_goal": props["Weekly goal"]["rich_text"][0]["plain_text"] if props["Weekly goal"]["rich_text"] else "", + "icon": page.get("icon", {}).get("emoji", "") + }) + + return projects + +def get_weekly_tasks(): + """Get tasks due this week that are not Done.""" + # Calculate date range for this week + today = datetime.now().date() + week_end = today + timedelta(days=7) + + results = notion.databases.query( + database_id=TASKS_DB, + filter={ + "and": [ + { + "property": "Status", + "status": { + "does_not_equal": "Done" + } + }, + { + "property": "Due date", + "date": { + "on_or_before": week_end.isoformat() + } + } + ] + }, + sorts=[ + { + "property": "Due date", + "direction": "ascending" + } + ] + ) + + tasks = [] + for page in results["results"]: + props = page["properties"] + + # Get due date + due_date = None + if props["Due date"]["date"]: + due_date = props["Due date"]["date"]["start"] + + # Get project name if linked + project_names = [] + if props["Project"]["relation"]: + for rel in props["Project"]["relation"]: + try: + project = notion.pages.retrieve(page_id=rel["id"]) + project_name = project["properties"]["Name"]["title"][0]["plain_text"] + project_names.append(project_name) + except: + pass + + # Get blocking status + has_blocking = len(props["Blocking"]["relation"]) > 0 if props["Blocking"]["relation"] else False + + tasks.append({ + "id": page["id"], + "url": page["url"], + "name": props["Task name"]["title"][0]["plain_text"] if props["Task name"]["title"] else "", + "due_date": due_date, + "status": props["Status"]["status"]["name"] if props["Status"]["status"] else "", + "priority": props["Priority"]["select"]["name"] if props["Priority"]["select"] else "", + "effort_level": props["Effort level"]["select"]["name"] if props["Effort level"]["select"] else "", + "energy_type": props["Energy type"]["select"]["name"] if props["Energy type"]["select"] else "", + "task_type": [t["name"] for t in props["Task type"]["multi_select"]], + "project": project_names[0] if project_names else "", + "has_blocking": has_blocking + }) + + return tasks + +def get_reading_suggestions(): + """Get articles with Status = 'To Read' or 'In Progress'.""" + results = notion.databases.query( + database_id=READING_LIST_DB, + filter={ + "or": [ + { + "property": "Status", + "status": { + "equals": "To Read" + } + }, + { + "property": "Status", + "status": { + "equals": "In Progress" + } + } + ] + } + ) + + articles = [] + for page in results["results"]: + props = page["properties"] + articles.append({ + "id": page["id"], + "url": props["URL"]["url"] if props["URL"]["url"] else "", + "title": props["Title"]["title"][0]["plain_text"] if props["Title"]["title"] else "", + "topic": [t["name"] for t in props["Topic"]["multi_select"]], + "read_time": props["Read time"]["select"]["name"] if props["Read time"]["select"] else "", + "status": props["Status"]["status"]["name"] if props["Status"]["status"] else "" + }) + + return articles + +def main(): + try: + data = { + "projects": get_focus_projects(), + "tasks": get_weekly_tasks(), + "reading": get_reading_suggestions(), + "generated_at": datetime.now().isoformat() + } + + print(json.dumps(data, indent=2)) + return 0 + + except Exception as e: + print(json.dumps({"error": str(e)}), file=sys.stderr) + return 1 + +if __name__ == "__main__": + sys.exit(main()) diff --git a/scripts/requirements.txt b/scripts/requirements.txt new file mode 100644 index 0000000..ebd8987 --- /dev/null +++ b/scripts/requirements.txt @@ -0,0 +1 @@ +notion-client>=2.0.0