Add goals, reading list, and people CRM - goals.py: Goal tracking with milestones and progress - reading.py: Reading list tracker (articles, books, papers) - people.py: Personal CRM for keeping notes on people - 21 tools total now
This commit is contained in:
163
tools/reading.py
Executable file
163
tools/reading.py
Executable file
@ -0,0 +1,163 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
reading - Reading list and article tracker
|
||||
|
||||
Track articles, books, and things to read.
|
||||
"""
|
||||
|
||||
import json
|
||||
import sys
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
WORKSPACE = Path("/home/wdjones/.openclaw/workspace")
|
||||
READING_FILE = WORKSPACE / "data" / "reading.json"
|
||||
|
||||
def load_list() -> list:
|
||||
"""Load reading list."""
|
||||
READING_FILE.parent.mkdir(parents=True, exist_ok=True)
|
||||
if READING_FILE.exists():
|
||||
with open(READING_FILE) as f:
|
||||
return json.load(f)
|
||||
return []
|
||||
|
||||
def save_list(items: list):
|
||||
"""Save reading list."""
|
||||
with open(READING_FILE, 'w') as f:
|
||||
json.dump(items, f, indent=2)
|
||||
|
||||
def add(title: str, url: str = None, item_type: str = 'article', tags: list = None):
|
||||
"""Add to reading list."""
|
||||
items = load_list()
|
||||
|
||||
item = {
|
||||
'id': len(items) + 1,
|
||||
'title': title,
|
||||
'url': url,
|
||||
'type': item_type,
|
||||
'tags': tags or [],
|
||||
'added': datetime.now().isoformat(),
|
||||
'status': 'unread',
|
||||
'notes': None,
|
||||
}
|
||||
|
||||
items.append(item)
|
||||
save_list(items)
|
||||
|
||||
emoji = {'article': '📄', 'book': '📚', 'video': '🎬', 'paper': '📑'}.get(item_type, '📄')
|
||||
print(f"{emoji} Added: {title}")
|
||||
|
||||
def show_list(status: str = None, limit: int = 20):
|
||||
"""Show reading list."""
|
||||
items = load_list()
|
||||
|
||||
if status:
|
||||
items = [i for i in items if i['status'] == status]
|
||||
|
||||
if not items:
|
||||
print("Reading list is empty")
|
||||
return
|
||||
|
||||
# Group by status
|
||||
unread = [i for i in items if i['status'] == 'unread']
|
||||
reading = [i for i in items if i['status'] == 'reading']
|
||||
done = [i for i in items if i['status'] == 'done']
|
||||
|
||||
print(f"\n📚 Reading List")
|
||||
print("=" * 50)
|
||||
|
||||
if reading:
|
||||
print(f"\n📖 Currently Reading ({len(reading)})")
|
||||
for item in reading:
|
||||
print(f" #{item['id']} {item['title'][:40]}")
|
||||
|
||||
if unread:
|
||||
print(f"\n📥 To Read ({len(unread)})")
|
||||
for item in unread[:10]:
|
||||
emoji = {'article': '📄', 'book': '📚', 'video': '🎬'}.get(item['type'], '📄')
|
||||
print(f" #{item['id']} {emoji} {item['title'][:40]}")
|
||||
if len(unread) > 10:
|
||||
print(f" ... and {len(unread) - 10} more")
|
||||
|
||||
if done:
|
||||
print(f"\n✅ Completed ({len(done)})")
|
||||
for item in done[-5:]:
|
||||
print(f" #{item['id']} {item['title'][:40]}")
|
||||
|
||||
print()
|
||||
|
||||
def start_reading(item_id: int):
|
||||
"""Mark item as currently reading."""
|
||||
items = load_list()
|
||||
for item in items:
|
||||
if item['id'] == item_id:
|
||||
item['status'] = 'reading'
|
||||
item['started'] = datetime.now().isoformat()
|
||||
save_list(items)
|
||||
print(f"📖 Started: {item['title']}")
|
||||
return
|
||||
print(f"Item not found: #{item_id}")
|
||||
|
||||
def finish_reading(item_id: int, notes: str = None):
|
||||
"""Mark item as done."""
|
||||
items = load_list()
|
||||
for item in items:
|
||||
if item['id'] == item_id:
|
||||
item['status'] = 'done'
|
||||
item['finished'] = datetime.now().isoformat()
|
||||
if notes:
|
||||
item['notes'] = notes
|
||||
save_list(items)
|
||||
print(f"✅ Finished: {item['title']}")
|
||||
return
|
||||
print(f"Item not found: #{item_id}")
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
show_list()
|
||||
return
|
||||
|
||||
cmd = sys.argv[1]
|
||||
|
||||
if cmd == 'add' and len(sys.argv) > 2:
|
||||
# Parse: add <title> [--url URL] [--type TYPE]
|
||||
args = sys.argv[2:]
|
||||
title_parts = []
|
||||
url = None
|
||||
item_type = 'article'
|
||||
|
||||
i = 0
|
||||
while i < len(args):
|
||||
if args[i] == '--url' and i + 1 < len(args):
|
||||
url = args[i + 1]
|
||||
i += 2
|
||||
elif args[i] == '--type' and i + 1 < len(args):
|
||||
item_type = args[i + 1]
|
||||
i += 2
|
||||
else:
|
||||
title_parts.append(args[i])
|
||||
i += 1
|
||||
|
||||
add(' '.join(title_parts), url, item_type)
|
||||
|
||||
elif cmd == 'start' and len(sys.argv) > 2:
|
||||
start_reading(int(sys.argv[2]))
|
||||
|
||||
elif cmd == 'done' and len(sys.argv) > 2:
|
||||
item_id = int(sys.argv[2])
|
||||
notes = ' '.join(sys.argv[3:]) if len(sys.argv) > 3 else None
|
||||
finish_reading(item_id, notes)
|
||||
|
||||
elif cmd == 'list':
|
||||
show_list()
|
||||
|
||||
else:
|
||||
print("Usage:")
|
||||
print(" reading - Show list")
|
||||
print(" reading add <title> - Add article")
|
||||
print(" reading add <title> --type book --url URL")
|
||||
print(" reading start <id> - Start reading")
|
||||
print(" reading done <id> [notes] - Finish reading")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user