Skip to content

Instantly share code, notes, and snippets.

@yabberyabber
Created June 7, 2018 00:59
Show Gist options
  • Save yabberyabber/144d865f2449b6e0904e4ef9b44ee208 to your computer and use it in GitHub Desktop.
Save yabberyabber/144d865f2449b6e0904e4ef9b44ee208 to your computer and use it in GitHub Desktop.
#! /usr/bin/env python3
"""
Simple pomodoro tracker that has been heavily customized to my personal needs.
Each pomodoro will wait for user input before continuing on meaning that
working a little long into your break does not reduce the duration of your
break (the break timer doesn't start until you press enter). When it is time
to move on, pomodoro will present a notification repeatedly using libnotify.
The default pomodoro schedule is ---|--- where each - is 25 minutes of work
followed by 5 minutes of break and where | is 25 minutes of break. A
different schedule can be passed in just by passing that string as the first
argument to the program. Some shells will try to parse | as a pipe so
long breaks can also be represented as 0.
> ./main.py ___0___
Represents the default arrangement. 3 work pomodoro, 1 break pomodoro, and 3
work pomodoro
> ./main.py _0__0_
1 work pomodoro, 1 break pomodoro, 2 work pomodoro, 1 break pomodoro, 1 work
pomodoro.
"""
import sys
import time
import subprocess
import threading
import itertools
import select
import datetime
def notify(summary, *body):
"""
Send a notification using the notify-send command (check that
libnotify is installed on local machine before running this)
"""
def child_process():
subprocess.Popen(['notify-send', summary, '\n'.join(body)])
t = threading.Thread(name='alarm', target=child_process,
args=())
t.start()
def clear_stdin():
"""
Flush standard in of any residual input
"""
while True:
if select.select([sys.stdin,], [], [], 0.0)[0]:
sys.stdin.readline()
else:
break
def delay(minutes):
"""
Wraper over time.sleep (wrapping this makes testing easier)
"""
time.sleep(minutes * 60)
class Pomodoro:
"""
Not a class; this is a namespace containing parameters that define pomodoro
"""
WORK = 1
REST = 2
LONG_REST = 3
DURATION = {
WORK: 25,
REST: 5,
LONG_REST: 25,
}
def untiltext(duration):
untiltime = datetime.datetime.now() + datetime.timedelta(minutes=duration)
return "(until {})".format(untiltime.strftime("%I:%M"))
def alarm(phase, duration, schedule, wait_for_awk=False):
"""
Send the user a notification repeatedly until they acknowledge the it. Do
not continue along the schedule until the notification is acknowledge (by
pressing enter)
"""
summary = "Begin {} for {} minutes".format(phase, duration)
print()
print(summary)
print(schedule)
print(untiltext(duration))
print("\n")
notify(summary, schedule, untiltext(duration))
clear_stdin()
while True:
print("Press enter to continue")
stdin, stdout, stderr = select.select([sys.stdin,], [], [], 30.0)
if stdin:
print("\tInput awknowledged... beginning phase")
clear_stdin()
break
notify(summary, schedule, untiltext(duration))
def parse_schedule(schedule):
"""
Take a string-representation of a pomodoro schedule and parse it into
a machine-representation. The machine representation is a list of
Pomodoro.WORK, Pomodoro.REST, or Pomodoro.LONG_REST.
"""
print("Pomodoro Schedule:")
result = []
duration_minutes = 0
for char in schedule:
if char in ['-', '_']:
result.append(Pomodoro.WORK)
duration_minutes += Pomodoro.DURATION[Pomodoro.WORK]
print("\tWork until {}".format(untiltext(duration_minutes)))
result.append(Pomodoro.REST)
duration_minutes += Pomodoro.DURATION[Pomodoro.REST]
print("\tShort rest until {}".format(untiltext(duration_minutes)))
elif char in ['|', ' ', '0']:
result.append(Pomodoro.LONG_REST)
duration_minutes += Pomodoro.DURATION[Pomodoro.LONG_REST]
print("\tLong rest until {}".format(untiltext(duration_minutes)))
else:
raise Exception(
("Invalid Schedule: '{}'. "
"Schedules can only contain _,|").format(
schedule))
print("\n\n")
return result
def stringatize_schedule(schedule, num_completed):
result = ""
for idx, item in enumerate(schedule):
if idx < num_completed:
result += " * "
elif item == Pomodoro.WORK:
result += " _ "
elif item == Pomodoro.REST:
pass
elif item == Pomodoro.LONG_REST:
result += " | "
return result
def pomodoros(schedule):
"""
Execute a given pomodoro schedule where a schedule is a
list of Pomodoro.WORK, Pomodoro.REST, or Pomodoro.LONG_REST
"""
completed_pomodoro = 0
for item in schedule:
duration = Pomodoro.DURATION[item]
schedule_str = stringatize_schedule(schedule, completed_pomodoro)
if item == Pomodoro.WORK:
alarm("work", duration, schedule_str, True)
if item == Pomodoro.REST:
alarm("short rest", duration, schedule_str, True)
completed_pomodoro += 1
if item == Pomodoro.LONG_REST:
alarm("long rest", duration, schedule_str, True)
completed_pomodoro += 1
delay(duration)
notify("Schedule completed")
if __name__ == "__main__":
schedule = sys.argv[1] if len(sys.argv) > 1 else '---|---'
pomodoros(parse_schedule(schedule))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment