138 lines
4.0 KiB
Python
Executable File
138 lines
4.0 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
Focus timer - simple pomodoro-style timer with logging.
|
|
"""
|
|
|
|
import sys
|
|
import time
|
|
import json
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
|
|
WORKSPACE = Path("/home/wdjones/.openclaw/workspace")
|
|
LOG_FILE = WORKSPACE / "memory" / "focus-log.json"
|
|
|
|
def load_log() -> list:
|
|
"""Load focus session log."""
|
|
if LOG_FILE.exists():
|
|
try:
|
|
with open(LOG_FILE) as f:
|
|
return json.load(f)
|
|
except:
|
|
return []
|
|
return []
|
|
|
|
def save_log(log: list):
|
|
"""Save focus session log."""
|
|
LOG_FILE.parent.mkdir(parents=True, exist_ok=True)
|
|
with open(LOG_FILE, 'w') as f:
|
|
json.dump(log, f, indent=2)
|
|
|
|
def format_time(seconds: int) -> str:
|
|
"""Format seconds as MM:SS."""
|
|
mins, secs = divmod(seconds, 60)
|
|
return f"{mins:02d}:{secs:02d}"
|
|
|
|
def timer(minutes: int, task: str = None):
|
|
"""Run a focus timer."""
|
|
total_seconds = minutes * 60
|
|
remaining = total_seconds
|
|
|
|
start_time = datetime.now()
|
|
print(f"\n🎯 Focus session started: {minutes} minutes")
|
|
if task:
|
|
print(f" Task: {task}")
|
|
print()
|
|
|
|
try:
|
|
while remaining > 0:
|
|
print(f"\r ⏱️ {format_time(remaining)} remaining ", end='', flush=True)
|
|
time.sleep(1)
|
|
remaining -= 1
|
|
|
|
print(f"\r ✅ Session complete! ")
|
|
print("\n🔔 Time's up! Take a break.\n")
|
|
|
|
# Log the session
|
|
log = load_log()
|
|
log.append({
|
|
'start': start_time.isoformat(),
|
|
'end': datetime.now().isoformat(),
|
|
'duration_minutes': minutes,
|
|
'task': task,
|
|
'completed': True
|
|
})
|
|
save_log(log)
|
|
|
|
except KeyboardInterrupt:
|
|
elapsed = total_seconds - remaining
|
|
print(f"\r ⏸️ Stopped after {format_time(elapsed)} ")
|
|
|
|
if elapsed > 60: # Log if > 1 minute
|
|
log = load_log()
|
|
log.append({
|
|
'start': start_time.isoformat(),
|
|
'end': datetime.now().isoformat(),
|
|
'duration_minutes': round(elapsed / 60, 1),
|
|
'task': task,
|
|
'completed': False
|
|
})
|
|
save_log(log)
|
|
print(" Session logged (partial)")
|
|
|
|
def stats():
|
|
"""Show focus statistics."""
|
|
log = load_log()
|
|
|
|
if not log:
|
|
print("No focus sessions logged yet.")
|
|
return
|
|
|
|
today = datetime.now().strftime("%Y-%m-%d")
|
|
today_sessions = [s for s in log if s['start'].startswith(today)]
|
|
|
|
total_minutes = sum(s['duration_minutes'] for s in log)
|
|
today_minutes = sum(s['duration_minutes'] for s in today_sessions)
|
|
completed = len([s for s in log if s.get('completed')])
|
|
|
|
print("\n📊 Focus Stats")
|
|
print(f" Total sessions: {len(log)}")
|
|
print(f" Completed: {completed}")
|
|
print(f" Total time: {total_minutes:.0f} minutes ({total_minutes/60:.1f} hours)")
|
|
print(f" Today: {len(today_sessions)} sessions, {today_minutes:.0f} minutes")
|
|
|
|
if today_sessions:
|
|
print("\n Today's sessions:")
|
|
for s in today_sessions[-5:]:
|
|
start = s['start'][11:16]
|
|
status = "✅" if s.get('completed') else "⏸️"
|
|
task = s.get('task', '')[:30] or 'No task'
|
|
print(f" {status} {start} - {s['duration_minutes']}min - {task}")
|
|
print()
|
|
|
|
def main():
|
|
if len(sys.argv) < 2:
|
|
print("Usage:")
|
|
print(" focus.py <minutes> [task] - Start a focus session")
|
|
print(" focus.py stats - Show statistics")
|
|
print("\nExamples:")
|
|
print(" focus.py 25 'Write documentation'")
|
|
print(" focus.py 15")
|
|
sys.exit(0)
|
|
|
|
if sys.argv[1] == 'stats':
|
|
stats()
|
|
return
|
|
|
|
try:
|
|
minutes = int(sys.argv[1])
|
|
except ValueError:
|
|
print("Minutes must be a number")
|
|
sys.exit(1)
|
|
|
|
task = ' '.join(sys.argv[2:]) if len(sys.argv) > 2 else None
|
|
timer(minutes, task)
|
|
|
|
if __name__ == "__main__":
|
|
main()
|