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:
173
tools/goals.py
Executable file
173
tools/goals.py
Executable file
@ -0,0 +1,173 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
goals - Goal and project tracking with milestones
|
||||
|
||||
Track long-term goals and break them into actionable steps.
|
||||
"""
|
||||
|
||||
import json
|
||||
import sys
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
WORKSPACE = Path("/home/wdjones/.openclaw/workspace")
|
||||
GOALS_FILE = WORKSPACE / "data" / "goals.json"
|
||||
|
||||
def load_goals() -> dict:
|
||||
"""Load goals data."""
|
||||
GOALS_FILE.parent.mkdir(parents=True, exist_ok=True)
|
||||
if GOALS_FILE.exists():
|
||||
with open(GOALS_FILE) as f:
|
||||
return json.load(f)
|
||||
return {'goals': []}
|
||||
|
||||
def save_goals(data: dict):
|
||||
"""Save goals data."""
|
||||
with open(GOALS_FILE, 'w') as f:
|
||||
json.dump(data, f, indent=2)
|
||||
|
||||
def add_goal(title: str, description: str = "", deadline: str = None):
|
||||
"""Add a new goal."""
|
||||
data = load_goals()
|
||||
|
||||
goal = {
|
||||
'id': len(data['goals']) + 1,
|
||||
'title': title,
|
||||
'description': description,
|
||||
'deadline': deadline,
|
||||
'created': datetime.now().isoformat(),
|
||||
'status': 'active',
|
||||
'milestones': [],
|
||||
'progress': 0,
|
||||
}
|
||||
|
||||
data['goals'].append(goal)
|
||||
save_goals(data)
|
||||
print(f"🎯 Goal #{goal['id']}: {title}")
|
||||
|
||||
def add_milestone(goal_id: int, title: str):
|
||||
"""Add a milestone to a goal."""
|
||||
data = load_goals()
|
||||
|
||||
for goal in data['goals']:
|
||||
if goal['id'] == goal_id:
|
||||
milestone = {
|
||||
'id': len(goal['milestones']) + 1,
|
||||
'title': title,
|
||||
'done': False,
|
||||
'created': datetime.now().isoformat(),
|
||||
}
|
||||
goal['milestones'].append(milestone)
|
||||
save_goals(data)
|
||||
print(f" ✓ Added milestone: {title}")
|
||||
return
|
||||
|
||||
print(f"Goal not found: #{goal_id}")
|
||||
|
||||
def complete_milestone(goal_id: int, milestone_id: int):
|
||||
"""Mark a milestone as complete."""
|
||||
data = load_goals()
|
||||
|
||||
for goal in data['goals']:
|
||||
if goal['id'] == goal_id:
|
||||
for m in goal['milestones']:
|
||||
if m['id'] == milestone_id:
|
||||
m['done'] = True
|
||||
m['completed'] = datetime.now().isoformat()
|
||||
|
||||
# Update progress
|
||||
done = len([x for x in goal['milestones'] if x['done']])
|
||||
total = len(goal['milestones'])
|
||||
goal['progress'] = int(done / total * 100) if total > 0 else 0
|
||||
|
||||
save_goals(data)
|
||||
print(f" ✓ Completed: {m['title']}")
|
||||
print(f" Progress: {goal['progress']}%")
|
||||
return
|
||||
|
||||
print("Milestone not found")
|
||||
|
||||
def show_goals(show_all: bool = False):
|
||||
"""Display all goals."""
|
||||
data = load_goals()
|
||||
|
||||
goals = data['goals']
|
||||
if not show_all:
|
||||
goals = [g for g in goals if g['status'] == 'active']
|
||||
|
||||
if not goals:
|
||||
print("No active goals. Add one with: goals add <title>")
|
||||
return
|
||||
|
||||
print(f"\n🎯 Goals ({len(goals)})")
|
||||
print("=" * 50)
|
||||
|
||||
for goal in goals:
|
||||
status = "✅" if goal['status'] == 'completed' else "🔵"
|
||||
bar = "█" * (goal['progress'] // 10) + "░" * (10 - goal['progress'] // 10)
|
||||
|
||||
print(f"\n{status} #{goal['id']} {goal['title']}")
|
||||
print(f" [{bar}] {goal['progress']}%")
|
||||
|
||||
if goal.get('deadline'):
|
||||
print(f" 📅 Deadline: {goal['deadline']}")
|
||||
|
||||
if goal['milestones']:
|
||||
for m in goal['milestones']:
|
||||
check = "✓" if m['done'] else "○"
|
||||
print(f" {check} {m['title']}")
|
||||
|
||||
print()
|
||||
|
||||
def complete_goal(goal_id: int):
|
||||
"""Mark a goal as completed."""
|
||||
data = load_goals()
|
||||
|
||||
for goal in data['goals']:
|
||||
if goal['id'] == goal_id:
|
||||
goal['status'] = 'completed'
|
||||
goal['progress'] = 100
|
||||
goal['completed'] = datetime.now().isoformat()
|
||||
save_goals(data)
|
||||
print(f"🎉 Goal completed: {goal['title']}")
|
||||
return
|
||||
|
||||
print(f"Goal not found: #{goal_id}")
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
show_goals()
|
||||
return
|
||||
|
||||
cmd = sys.argv[1]
|
||||
|
||||
if cmd == 'add' and len(sys.argv) > 2:
|
||||
title = ' '.join(sys.argv[2:])
|
||||
add_goal(title)
|
||||
|
||||
elif cmd == 'milestone' and len(sys.argv) > 3:
|
||||
goal_id = int(sys.argv[2])
|
||||
title = ' '.join(sys.argv[3:])
|
||||
add_milestone(goal_id, title)
|
||||
|
||||
elif cmd == 'done' and len(sys.argv) > 3:
|
||||
goal_id = int(sys.argv[2])
|
||||
milestone_id = int(sys.argv[3])
|
||||
complete_milestone(goal_id, milestone_id)
|
||||
|
||||
elif cmd == 'complete' and len(sys.argv) > 2:
|
||||
complete_goal(int(sys.argv[2]))
|
||||
|
||||
elif cmd == 'list':
|
||||
show_goals(show_all='--all' in sys.argv)
|
||||
|
||||
else:
|
||||
print("Usage:")
|
||||
print(" goals - Show active goals")
|
||||
print(" goals add <title> - Add new goal")
|
||||
print(" goals milestone <id> <text> - Add milestone")
|
||||
print(" goals done <goal> <milestone> - Complete milestone")
|
||||
print(" goals complete <id> - Complete goal")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user