Add streak viewer - streak.py: Unified view of all streaks (habits, journal, notes, time) - 34 tools!

This commit is contained in:
2026-01-30 23:58:58 -06:00
parent b0f352128c
commit 47e15e4476
3 changed files with 142 additions and 0 deletions

View File

@ -24,6 +24,11 @@
"value": 33.0,
"timestamp": "2026-01-30T23:58:26.472974",
"note": "Past midnight"
},
{
"value": 34.0,
"timestamp": "2026-01-30T23:58:58.553542",
"note": "Last before midnight!"
}
],
"created": "2026-01-30T23:54:10.266015"

136
tools/streak.py Executable file
View File

@ -0,0 +1,136 @@
#!/usr/bin/env python3
"""
streak - View all streaks across tools
Unified view of habit streaks, journal streaks, etc.
"""
import json
from datetime import datetime, timedelta
from pathlib import Path
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 check_date_exists(data: dict | list, date: str, key: str = None) -> bool:
"""Check if an entry exists for a date."""
if isinstance(data, dict):
if key:
return date in data.get(key, {})
return date in data
return False
def calculate_streak(check_func) -> int:
"""Calculate a streak based on a check function."""
streak = 0
date = datetime.now()
while True:
date_str = date.strftime("%Y-%m-%d")
if check_func(date_str):
streak += 1
date -= timedelta(days=1)
else:
break
return streak
def get_habit_streak(habit_name: str) -> int:
"""Get streak for a habit."""
data = load_json(WORKSPACE / "data" / "habits.json")
log = data.get('log', {})
def check(date):
return habit_name in log.get(date, [])
return calculate_streak(check)
def get_journal_streak() -> int:
"""Get journal writing streak."""
journal_dir = WORKSPACE / "journal"
if not journal_dir.exists():
return 0
def check(date):
year, month, day = date.split('-')
path = journal_dir / year / month / f"{date}.json"
if path.exists():
data = load_json(path)
return len(data.get('entries', [])) > 0
return False
return calculate_streak(check)
def get_notes_streak() -> int:
"""Get daily notes streak."""
memory_dir = WORKSPACE / "memory"
if not memory_dir.exists():
return 0
def check(date):
path = memory_dir / f"{date}.md"
return path.exists()
return calculate_streak(check)
def get_time_tracking_streak() -> int:
"""Get time tracking streak."""
data = load_json(WORKSPACE / "data" / "timetrack.json")
entries = data.get('entries', [])
dates_tracked = set()
for e in entries:
date = e['start'][:10]
dates_tracked.add(date)
def check(date):
return date in dates_tracked
return calculate_streak(check)
def show_streaks():
"""Show all streaks."""
print("\n🔥 Streaks")
print("=" * 40)
# Notes streak
notes = get_notes_streak()
print(f" 📝 Daily Notes: {notes} day{'s' if notes != 1 else ''}")
# Journal streak
journal = get_journal_streak()
print(f" 📔 Journal: {journal} day{'s' if journal != 1 else ''}")
# Time tracking streak
time_track = get_time_tracking_streak()
print(f" ⏱️ Time Tracking: {time_track} day{'s' if time_track != 1 else ''}")
# Habit streaks
habits_data = load_json(WORKSPACE / "data" / "habits.json")
habits = habits_data.get('habits', {})
if habits:
print(f"\n 📊 Habits:")
for name, info in habits.items():
if info.get('active', True):
streak = get_habit_streak(name)
if streak > 0:
print(f" {name}: {streak} day{'s' if streak != 1 else ''} 🔥")
else:
print(f" {name}: 0 days")
print()
def main():
show_streaks()
if __name__ == "__main__":
main()

1
ws
View File

@ -49,6 +49,7 @@ COMMANDS = {
'timer': ('tools/timer.py', 'Countdown and stopwatch'),
'gen': ('tools/gen.py', 'Password, UUID, lorem, etc.'),
'fortune': ('tools/fortune.py', 'Random wisdom'),
'streak': ('tools/streak.py', 'View all streaks'),
# Projects
'news': ('projects/news-feed/main.py', 'RSS news reader'),