From 1a3a2117c41d53e8bd7c0063b0743201ca43123a Mon Sep 17 00:00:00 2001 From: Beast Date: Mon, 15 Jun 2026 16:27:22 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20extract=5Ftodos=20=E9=80=82=E9=85=8D?= =?UTF-8?q?=E6=96=B0=E7=89=88=20digest=20=E6=A0=BC=E5=BC=8F\n\n-=20?= =?UTF-8?q?=E9=80=92=E5=BD=92=E6=90=9C=E7=B4=A2=E6=97=A5=E6=9C=9F=E5=AD=90?= =?UTF-8?q?=E7=9B=AE=E5=BD=95=EF=BC=88daily/YYYY-MM-DD/=EF=BC=89\n-=20?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E5=90=8D=E5=8C=B9=E9=85=8D=E4=BB=8E=20=5Fdig?= =?UTF-8?q?est.md=20=E6=94=B9=E4=B8=BA=E5=8C=85=E5=90=AB=20=5Fdigest=20?= =?UTF-8?q?=E7=9A=84=20.md\n-=20=E6=96=B0=E5=A2=9E=E6=95=B0=E5=AD=97?= =?UTF-8?q?=E5=88=97=E8=A1=A8=E6=A0=BC=E5=BC=8F=E5=8C=B9=E9=85=8D=EF=BC=88?= =?UTF-8?q?1.=20**bold**:=20desc=EF=BC=89\n-=20=E6=96=B0=E5=A2=9E=20dash?= =?UTF-8?q?=20=E5=88=97=E8=A1=A8=E6=A0=BC=E5=BC=8F=E5=8C=B9=E9=85=8D?= =?UTF-8?q?=EF=BC=88-=20**bold**:=20desc=EF=BC=89\n-=20=E8=BF=87=E6=BB=A4?= =?UTF-8?q?=E6=A8=A1=E6=9D=BF=E6=A0=87=E9=A2=98=EF=BC=88=E3=80=90xxx?= =?UTF-8?q?=E3=80=91=E7=9F=AD=E6=A0=87=E9=A2=98=EF=BC=89\n-=20=E5=8F=AA?= =?UTF-8?q?=E4=BB=8E=E5=BE=85=E5=8A=9E=E4=BA=8B=E9=A1=B9=20section=20?= =?UTF-8?q?=E5=BC=80=E5=A7=8B=E6=8F=90=E5=8F=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/extract_todos.py | 80 +++++++++++++++++++++++++++++++++++----- 1 file changed, 71 insertions(+), 9 deletions(-) diff --git a/scripts/extract_todos.py b/scripts/extract_todos.py index 61a9dc4..14f9b33 100755 --- a/scripts/extract_todos.py +++ b/scripts/extract_todos.py @@ -1,4 +1,9 @@ -"""Extract todos from the latest inspiration digest + sync to todo API.""" +"""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 @@ -9,33 +14,88 @@ TODO_API = "http://127.0.0.1:9001/api/todos" def get_latest_digest(): - """Find the most recent digest file.""" + """Find the most recent digest file (checks date subdirectories).""" if not os.path.isdir(DIGEST_DIR): return None - files = [f for f in os.listdir(DIGEST_DIR) if f.endswith("_digest.md")] + 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(reverse=True) - return os.path.join(DIGEST_DIR, files[0]) + 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.""" + """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 = [] - # Match markdown checkboxes: - [ ] something + 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: - todos.append(m.group(1).strip()) + 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).""" - # Get existing todos try: resp = requests.get(TODO_API, timeout=5) existing = [t["text"] for t in resp.json().get("todos", [])] @@ -59,5 +119,7 @@ if __name__ == "__main__": 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")