206 lines
5.5 KiB
Python
Executable File
206 lines
5.5 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
journal - Structured daily journaling
|
|
|
|
Guided prompts for daily reflection and growth.
|
|
"""
|
|
|
|
import json
|
|
import sys
|
|
from datetime import datetime, timedelta
|
|
from pathlib import Path
|
|
|
|
WORKSPACE = Path("/home/wdjones/.openclaw/workspace")
|
|
JOURNAL_DIR = WORKSPACE / "journal"
|
|
|
|
PROMPTS = {
|
|
'morning': [
|
|
"What am I grateful for today?",
|
|
"What would make today great?",
|
|
"What am I looking forward to?",
|
|
],
|
|
'evening': [
|
|
"What went well today?",
|
|
"What could have gone better?",
|
|
"What did I learn?",
|
|
"What am I grateful for?",
|
|
],
|
|
'weekly': [
|
|
"What were this week's wins?",
|
|
"What challenges did I face?",
|
|
"What do I want to focus on next week?",
|
|
"How am I progressing on my goals?",
|
|
],
|
|
}
|
|
|
|
def get_journal_path(date: datetime = None) -> Path:
|
|
"""Get the journal file path for a date."""
|
|
if date is None:
|
|
date = datetime.now()
|
|
year = date.strftime("%Y")
|
|
month = date.strftime("%m")
|
|
day = date.strftime("%d")
|
|
|
|
path = JOURNAL_DIR / year / month
|
|
path.mkdir(parents=True, exist_ok=True)
|
|
return path / f"{date.strftime('%Y-%m-%d')}.json"
|
|
|
|
def load_entry(date: datetime = None) -> dict:
|
|
"""Load a journal entry."""
|
|
path = get_journal_path(date)
|
|
if path.exists():
|
|
with open(path) as f:
|
|
return json.load(f)
|
|
return {
|
|
'date': (date or datetime.now()).strftime("%Y-%m-%d"),
|
|
'entries': [],
|
|
}
|
|
|
|
def save_entry(entry: dict, date: datetime = None):
|
|
"""Save a journal entry."""
|
|
path = get_journal_path(date)
|
|
with open(path, 'w') as f:
|
|
json.dump(entry, f, indent=2)
|
|
|
|
def morning():
|
|
"""Morning journal routine."""
|
|
print("\n🌅 Morning Journal")
|
|
print("=" * 40)
|
|
print(f"Date: {datetime.now().strftime('%A, %B %d, %Y')}\n")
|
|
|
|
entry = load_entry()
|
|
responses = []
|
|
|
|
for i, prompt in enumerate(PROMPTS['morning'], 1):
|
|
print(f"{i}. {prompt}")
|
|
response = input(" → ").strip()
|
|
if response:
|
|
responses.append({'prompt': prompt, 'response': response})
|
|
print()
|
|
|
|
if responses:
|
|
entry['entries'].append({
|
|
'type': 'morning',
|
|
'time': datetime.now().isoformat(),
|
|
'responses': responses,
|
|
})
|
|
save_entry(entry)
|
|
print("✓ Morning journal saved")
|
|
|
|
def evening():
|
|
"""Evening journal routine."""
|
|
print("\n🌙 Evening Reflection")
|
|
print("=" * 40)
|
|
print(f"Date: {datetime.now().strftime('%A, %B %d, %Y')}\n")
|
|
|
|
entry = load_entry()
|
|
responses = []
|
|
|
|
for i, prompt in enumerate(PROMPTS['evening'], 1):
|
|
print(f"{i}. {prompt}")
|
|
response = input(" → ").strip()
|
|
if response:
|
|
responses.append({'prompt': prompt, 'response': response})
|
|
print()
|
|
|
|
if responses:
|
|
entry['entries'].append({
|
|
'type': 'evening',
|
|
'time': datetime.now().isoformat(),
|
|
'responses': responses,
|
|
})
|
|
save_entry(entry)
|
|
print("✓ Evening reflection saved")
|
|
|
|
def quick(text: str):
|
|
"""Quick journal entry."""
|
|
entry = load_entry()
|
|
entry['entries'].append({
|
|
'type': 'note',
|
|
'time': datetime.now().isoformat(),
|
|
'text': text,
|
|
})
|
|
save_entry(entry)
|
|
print(f"✓ Journal entry saved")
|
|
|
|
def show(days_ago: int = 0):
|
|
"""Show a journal entry."""
|
|
date = datetime.now() - timedelta(days=days_ago)
|
|
entry = load_entry(date)
|
|
|
|
print(f"\n📔 Journal: {entry['date']}")
|
|
print("=" * 40)
|
|
|
|
if not entry['entries']:
|
|
print("No entries for this day")
|
|
return
|
|
|
|
for item in entry['entries']:
|
|
time = item['time'][11:16]
|
|
|
|
if item['type'] == 'note':
|
|
print(f"\n[{time}] 📝 {item['text']}")
|
|
|
|
elif item['type'] in ('morning', 'evening'):
|
|
emoji = '🌅' if item['type'] == 'morning' else '🌙'
|
|
print(f"\n[{time}] {emoji} {item['type'].title()}")
|
|
for resp in item.get('responses', []):
|
|
print(f" Q: {resp['prompt']}")
|
|
print(f" A: {resp['response']}")
|
|
|
|
print()
|
|
|
|
def stats():
|
|
"""Show journaling statistics."""
|
|
total = 0
|
|
streak = 0
|
|
current_streak = True
|
|
|
|
date = datetime.now()
|
|
for i in range(365):
|
|
check_date = date - timedelta(days=i)
|
|
entry = load_entry(check_date)
|
|
|
|
if entry['entries']:
|
|
total += 1
|
|
if current_streak:
|
|
streak += 1
|
|
else:
|
|
current_streak = False
|
|
|
|
print(f"\n📊 Journal Stats")
|
|
print("=" * 40)
|
|
print(f" Current streak: {streak} days")
|
|
print(f" Total entries: {total} (last 365 days)")
|
|
print()
|
|
|
|
def main():
|
|
if len(sys.argv) < 2:
|
|
print("Usage:")
|
|
print(" journal morning - Morning prompts")
|
|
print(" journal evening - Evening reflection")
|
|
print(" journal <text> - Quick entry")
|
|
print(" journal show [days] - Show entry (0=today)")
|
|
print(" journal stats - Statistics")
|
|
show(0)
|
|
return
|
|
|
|
cmd = sys.argv[1]
|
|
|
|
if cmd == 'morning':
|
|
morning()
|
|
elif cmd == 'evening':
|
|
evening()
|
|
elif cmd == 'show':
|
|
days = int(sys.argv[2]) if len(sys.argv) > 2 else 0
|
|
show(days)
|
|
elif cmd == 'stats':
|
|
stats()
|
|
else:
|
|
# Quick entry
|
|
text = ' '.join(sys.argv[1:])
|
|
quick(text)
|
|
|
|
if __name__ == "__main__":
|
|
main()
|