From e5d5f09888522061ae90e44fe2fde2f5bd7e2aba Mon Sep 17 00:00:00 2001 From: Case Date: Fri, 30 Jan 2026 23:59:42 -0600 Subject: [PATCH] Add weekly review - review.py: Aggregates stats from all tools - 35 tools! --- data/metrics.json | 5 ++ tools/review.py | 128 ++++++++++++++++++++++++++++++++++++++++++++++ ws | 1 + 3 files changed, 134 insertions(+) create mode 100755 tools/review.py diff --git a/data/metrics.json b/data/metrics.json index 88f69f0..76a35db 100644 --- a/data/metrics.json +++ b/data/metrics.json @@ -29,6 +29,11 @@ "value": 34.0, "timestamp": "2026-01-30T23:58:58.553542", "note": "Last before midnight!" + }, + { + "value": 35.0, + "timestamp": "2026-01-30T23:59:42.953413", + "note": null } ], "created": "2026-01-30T23:54:10.266015" diff --git a/tools/review.py b/tools/review.py new file mode 100755 index 0000000..3f8937c --- /dev/null +++ b/tools/review.py @@ -0,0 +1,128 @@ +#!/usr/bin/env python3 +""" +review - Weekly and monthly review summaries + +Aggregate stats and insights across all tools. +""" + +import json +from datetime import datetime, timedelta +from pathlib import Path +from collections import Counter + +WORKSPACE = Path("/home/wdjones/.openclaw/workspace") + +def load_json(path: Path): + if path.exists(): + try: + with open(path) as f: + return json.load(f) + except: + pass + return {} + +def weekly_review(): + """Generate weekly review.""" + now = datetime.now() + week_ago = now - timedelta(days=7) + + print(f"\nšŸ“Š Weekly Review") + print(f" {week_ago.strftime('%b %d')} - {now.strftime('%b %d, %Y')}") + print("=" * 50) + + # Time tracked + time_data = load_json(WORKSPACE / "data" / "timetrack.json") + entries = [e for e in time_data.get('entries', []) + if datetime.fromisoformat(e['start']) > week_ago] + total_mins = sum(e['duration_min'] for e in entries) + + print(f"\nā±ļø Time Tracked: {total_mins:.0f} minutes ({total_mins/60:.1f} hours)") + + # By project + by_project = Counter() + for e in entries: + by_project[e.get('project') or 'general'] += e['duration_min'] + + if by_project: + print(" By project:") + for proj, mins in by_project.most_common(5): + print(f" {proj}: {mins:.0f}m") + + # Habits + habits_data = load_json(WORKSPACE / "data" / "habits.json") + habits = habits_data.get('habits', {}) + log = habits_data.get('log', {}) + + habit_completion = {} + for habit_name in habits: + if not habits[habit_name].get('active', True): + continue + done = 0 + for i in range(7): + date = (now - timedelta(days=i)).strftime("%Y-%m-%d") + if habit_name in log.get(date, []): + done += 1 + habit_completion[habit_name] = done + + if habit_completion: + print(f"\nāœ… Habits (days completed / 7):") + for habit, done in sorted(habit_completion.items(), key=lambda x: -x[1]): + bar = "ā–ˆ" * done + "ā–‘" * (7 - done) + print(f" {habit}: [{bar}] {done}/7") + + # Goals progress + goals_data = load_json(WORKSPACE / "data" / "goals.json") + goals = goals_data.get('goals', []) + active_goals = [g for g in goals if g['status'] == 'active'] + + if active_goals: + print(f"\nšŸŽÆ Goals:") + for goal in active_goals: + print(f" {goal['title'][:30]}: {goal['progress']}%") + + # Captures/ideas added + captures = load_json(WORKSPACE / "inbox" / "captures.json") + if isinstance(captures, list): + recent = [c for c in captures + if datetime.fromisoformat(c['captured']) > week_ago] + if recent: + print(f"\nšŸ“„ Captured: {len(recent)} items") + + ideas_data = load_json(WORKSPACE / "data" / "ideas.json") + if isinstance(ideas_data, list): + recent_ideas = [i for i in ideas_data + if datetime.fromisoformat(i['created']) > week_ago] + if recent_ideas: + print(f"šŸ’” New Ideas: {len(recent_ideas)}") + + # Notes + notes_count = 0 + for i in range(7): + date = (now - timedelta(days=i)).strftime("%Y-%m-%d") + path = WORKSPACE / "memory" / f"{date}.md" + if path.exists(): + notes_count += 1 + + print(f"\nšŸ“ Days with notes: {notes_count}/7") + + # Gratitude + gratitude = load_json(WORKSPACE / "data" / "gratitude.json") + if isinstance(gratitude, list): + recent = [g for g in gratitude + if datetime.fromisoformat(g['date']) > week_ago] + if recent: + print(f"šŸ™ Gratitude entries: {len(recent)}") + + print("\n" + "=" * 50) + print() + +def main(): + import sys + + if len(sys.argv) > 1 and sys.argv[1] == 'monthly': + print("Monthly review coming soon...") + else: + weekly_review() + +if __name__ == "__main__": + main() diff --git a/ws b/ws index d20a9d1..94e9f04 100755 --- a/ws +++ b/ws @@ -50,6 +50,7 @@ COMMANDS = { 'gen': ('tools/gen.py', 'Password, UUID, lorem, etc.'), 'fortune': ('tools/fortune.py', 'Random wisdom'), 'streak': ('tools/streak.py', 'View all streaks'), + 'review': ('tools/review.py', 'Weekly/monthly review'), # Projects 'news': ('projects/news-feed/main.py', 'RSS news reader'),