Files
inspiration-collector/scripts/extract_todos.py

126 lines
3.9 KiB
Python
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Extract todos from the latest inspiration digest + sync to todo API.
Supports multiple formats:
- [ ] checkbox style
- 1. **bold**: description (numbered list with bold prefix)
- - **bold**: description (dash list with bold prefix)
"""
import json
import os
import re
import requests
DIGEST_DIR = os.path.expanduser("~/inspiration-collector/ai-insights/daily")
TODO_API = "http://127.0.0.1:9001/api/todos"
def get_latest_digest():
"""Find the most recent digest file (checks date subdirectories)."""
if not os.path.isdir(DIGEST_DIR):
return None
files = []
for root, dirs, filenames in os.walk(DIGEST_DIR):
for f in filenames:
if "_digest" in f and f.endswith(".md"):
files.append(os.path.join(root, f))
if not files:
return None
files.sort(key=lambda x: os.path.getmtime(x), reverse=True)
return files[0]
def extract_todos(filepath):
"""Extract todo items from the digest markdown.
Matches:
1. [ ] checkbox items
2. Numbered items like "1. **bold title**description"
3. Dash items like "- **bold title**description"
Only extracts from the "待办事项" section onwards.
"""
with open(filepath) as f:
content = f.read()
todos = []
in_todo_section = False
for line in content.split("\n"):
stripped = line.strip()
# Detect start of todo section
if re.search(r"待办事项|待办|TODO|Todo", stripped) and not in_todo_section:
in_todo_section = True
continue
# End of todo section: next major heading or empty line after content
if in_todo_section and stripped.startswith("## ") and "待办" not in stripped:
break
if in_todo_section and stripped == "" and todos:
# Allow one blank line, but stop at second
if todos and todos[-1] == "__blank__":
todos.pop() # remove previous blank marker
break
todos.append("__blank__")
continue
if not in_todo_section:
continue
# Pattern 1: [ ] checkbox
m = re.match(r"\s*[-*]\s*\[\s*[ ]\s*\]\s*(.+)", line)
if m:
text = re.sub(r"\*\*", "", m.group(1).strip()) # strip bold markers
if text:
todos.append(text)
continue
# Pattern 2: numbered list "1. **bold**description" or "1. description"
m = re.match(r"\s*\d+\.\s+(.+)", line)
if m:
text = re.sub(r"\*\*", "", m.group(1).strip()) # strip bold markers
if text:
todos.append(text)
continue
# Pattern 3: dash list "- **bold**description"
m = re.match(r"\s*[-*]\s+(.+)", line)
if m:
text = re.sub(r"\*\*", "", m.group(1).strip())
# Skip template headers like 【今日输入】
if text and len(text) > 8 and not re.match(r"^【.+】$", text):
todos.append(text)
continue
# Clean up blank markers
todos = [t for t in todos if t != "__blank__"]
return todos
def push_todos(todos):
"""Send extracted todos to the API (skip if already exists)."""
try:
resp = requests.get(TODO_API, timeout=5)
existing = [t["text"] for t in resp.json().get("todos", [])]
except:
existing = []
count = 0
for text in todos:
if text not in existing:
requests.post(TODO_API, json={"text": text, "source": "inspiration"}, timeout=5)
count += 1
return count
if __name__ == "__main__":
filepath = get_latest_digest()
if not filepath:
print("No digest found")
exit(0)
todos = extract_todos(filepath)
print(f"Found {len(todos)} todos in {os.path.basename(filepath)}")
if todos:
for t in todos:
print(f" - {t[:60]}")
pushed = push_todos(todos)
print(f"Pushed {pushed} new todos to API")