5 changed files with 137 additions and 4 deletions
@ -0,0 +1 @@ |
|||||
|
version = "0.1.0" |
@ -0,0 +1,133 @@ |
|||||
|
#!/Users/gl6/env/bin/python |
||||
|
|
||||
|
import os |
||||
|
import sys |
||||
|
import time |
||||
|
from datetime import datetime, timedelta |
||||
|
import subprocess |
||||
|
from pathlib import Path |
||||
|
import argparse |
||||
|
from rich.console import Console |
||||
|
from rich.live import Live |
||||
|
from rich.theme import Theme |
||||
|
|
||||
|
ALARM_SOUND = os.path.expanduser("~/.pom/alarm.mp3") |
||||
|
ICON_IMAGE = os.path.expanduser("~/.pom/icon.png") |
||||
|
HISTORY_FILE = os.path.expanduser("~/.pom/history.txt") |
||||
|
|
||||
|
solarized_theme = Theme({ |
||||
|
"orange": "#cb4b16", |
||||
|
"violet": "#6c71c4", |
||||
|
}) |
||||
|
|
||||
|
console = Console(highlight=False, theme=solarized_theme) |
||||
|
|
||||
|
|
||||
|
def notify(): |
||||
|
console.print("Pomodoro finished!\a") |
||||
|
subprocess.run([ |
||||
|
"osascript", "-e", |
||||
|
f'display dialog "Pomodoro finished!" with title "Pomodoro" buttons {{"OK"}} default button "OK" with icon POSIX file "{ICON_IMAGE}"' |
||||
|
], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) |
||||
|
|
||||
|
|
||||
|
def get_ordinal(n): |
||||
|
if 10 <= n % 100 <= 20: |
||||
|
suffix = 'th' |
||||
|
else: |
||||
|
suffix = {1: 'st', 2: 'nd', 3: 'rd'}.get(n % 10, 'th') |
||||
|
return str(n) + suffix |
||||
|
|
||||
|
|
||||
|
def countdown(seconds, task): |
||||
|
with Live(console=console, refresh_per_second=4) as live: |
||||
|
while seconds > 0: |
||||
|
h, m, s = seconds // 3600, (seconds // 60) % 60, seconds % 60 |
||||
|
live.update(f"[orange]{h:02}:{m:02}:{s:02}[/] [white]{task}[/]") |
||||
|
time.sleep(1) |
||||
|
seconds -= 1 |
||||
|
console.print() |
||||
|
|
||||
|
|
||||
|
def record_task(seconds, task): |
||||
|
now = datetime.now() |
||||
|
date = now.strftime(f"%a {get_ordinal(now.day)} %B") |
||||
|
start_time = now.strftime("%H:%M") |
||||
|
end_time = (now + timedelta(seconds=seconds)).strftime("%H:%M") |
||||
|
h, m, s = seconds // 3600, (seconds // 60) % 60, seconds % 60 |
||||
|
with open(HISTORY_FILE, "a") as f: |
||||
|
f.write(f"{date} {start_time}>>{end_time} {h:02}:{m:02} {task}\n") |
||||
|
|
||||
|
|
||||
|
def show_history(): |
||||
|
if not Path(HISTORY_FILE).exists(): |
||||
|
console.print("No history found.") |
||||
|
else: |
||||
|
with open(HISTORY_FILE, "r") as f: |
||||
|
for line in f: |
||||
|
day, date, month, time, pom, task = line.strip().split(" ", 5) |
||||
|
content, note = task.split(" - ", 1) if " - " in task else (task, "") |
||||
|
note = "- " + note if note else "" |
||||
|
console.print(f"[blue]{day} {date} {month}[/] [green]{time}[/] [orange]{pom}[/] [white]{content}[/] [red]{note}[/]", highlight=False) |
||||
|
|
||||
|
|
||||
|
def clear_history(): |
||||
|
with open(HISTORY_FILE, "w") as f: |
||||
|
f.write("") |
||||
|
|
||||
|
|
||||
|
def edit_history(): |
||||
|
if not Path(HISTORY_FILE).exists(): |
||||
|
console.print("No history found.") |
||||
|
else: |
||||
|
subprocess.run(["vim", HISTORY_FILE]) |
||||
|
|
||||
|
|
||||
|
def main(): |
||||
|
parser = argparse.ArgumentParser(description="Pomodoro CLI tool.") |
||||
|
subparsers = parser.add_subparsers(dest="command") |
||||
|
|
||||
|
start_parser = subparsers.add_parser("start", help="Start a Pomodoro session.") |
||||
|
start_parser.add_argument("task", type=str, help="Task description.") |
||||
|
start_parser.add_argument("minutes", type=int, nargs="?", default=25, help="Duration of the Pomodoro in minutes.") |
||||
|
|
||||
|
subparsers.add_parser("show", help="Show Pomodoro history.") |
||||
|
subparsers.add_parser("clear", help="Clear Pomodoro history.") |
||||
|
subparsers.add_parser("edit", help="Edit Pomodoro history.") |
||||
|
|
||||
|
args = parser.parse_args() |
||||
|
|
||||
|
if not args.command: |
||||
|
parser.print_help() |
||||
|
sys.exit(1) |
||||
|
|
||||
|
if args.command == "start": |
||||
|
task = args.task |
||||
|
minutes = args.minutes |
||||
|
seconds = minutes * 60 |
||||
|
|
||||
|
try: |
||||
|
console.clear() |
||||
|
countdown(seconds, task) |
||||
|
notify() |
||||
|
record_task(seconds, task) |
||||
|
except KeyboardInterrupt: |
||||
|
record_task(seconds, f"{task} - cancelled") |
||||
|
sys.exit(0) |
||||
|
|
||||
|
elif args.command == "show": |
||||
|
show_history() |
||||
|
|
||||
|
elif args.command == "clear": |
||||
|
clear_history() |
||||
|
|
||||
|
elif args.command == "edit": |
||||
|
edit_history() |
||||
|
|
||||
|
else: |
||||
|
console.print("Invalid command") |
||||
|
sys.exit(1) |
||||
|
|
||||
|
|
||||
|
if __name__ == "__main__": |
||||
|
main() |
@ -1 +1,2 @@ |
|||||
rich>=13.8.0 |
rich>=13.8.0 |
||||
|
rumps>=0.4.0 |
||||
|
Loading…
Reference in new issue