#!/usr/bin/env python3 """ Fetch candidate tasks for evening planning. Returns JSON with all open (non-Done, non-Blocked) tasks, for local ranking by blocking status, "Suggested for today", and due date. """ import json import sys from datetime import datetime from notion_helpers import notion, get_data_source_id, query_all, TASKS_DB def get_evening_tasks(): """Get all tasks that are not Done and not Blocked.""" pages = query_all( get_data_source_id(TASKS_DB), filter={ "and": [ { "property": "Status", "status": { "does_not_equal": "Done" } }, { "property": "Status", "status": { "does_not_equal": "Blocked" } } ] }, sorts=[ { "property": "Due date", "direction": "ascending" } ] ) tasks = [] for page in pages: props = page["properties"] due_date = None if props["Due date"]["date"]: due_date = props["Due date"]["date"]["start"] suggested_for_today = None if props["Suggested for today"]["date"]: suggested_for_today = props["Suggested for today"]["date"]["start"] 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 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, "suggested_for_today": suggested_for_today, "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, "top_task": props["Top Task"]["checkbox"] }) return tasks def get_marked_top_tasks(): """Get all tasks currently marked Top Task = true, regardless of status. Queried separately (without the Done/Blocked exclusion) so a stale Top Task left checked on a now-Done task is still found and can be unmarked. """ pages = query_all( get_data_source_id(TASKS_DB), filter={ "property": "Top Task", "checkbox": { "equals": True } } ) return [ { "id": page["id"], "name": page["properties"]["Task name"]["title"][0]["plain_text"] if page["properties"]["Task name"]["title"] else "", "status": page["properties"]["Status"]["status"]["name"] if page["properties"]["Status"]["status"] else "" } for page in pages ] def main(): try: data = { "tasks": get_evening_tasks(), "marked_top_tasks": get_marked_top_tasks(), "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())