2020-10-10 04:13:16 +00:00
|
|
|
import sys
|
2020-07-05 19:41:04 +00:00
|
|
|
import csv
|
2021-05-02 17:01:42 +00:00
|
|
|
# This is a local script are we supposed to indicate that?
|
|
|
|
import prompt_window
|
2020-07-05 16:55:18 +00:00
|
|
|
import time
|
2020-07-05 19:41:04 +00:00
|
|
|
from datetime import datetime, timedelta
|
2020-07-06 03:40:12 +00:00
|
|
|
from pathlib import Path
|
2020-07-15 14:09:58 +00:00
|
|
|
from pytz import timezone
|
2020-10-10 03:44:46 +00:00
|
|
|
from playsound import playsound
|
2020-07-05 16:55:18 +00:00
|
|
|
|
|
|
|
import pytz
|
|
|
|
|
2020-07-06 13:40:55 +00:00
|
|
|
WORK_MIN = 25 # Minutes for a work session
|
|
|
|
SHORT_MIN = 5 # Minutes for a short break
|
2020-07-06 13:26:43 +00:00
|
|
|
LONG_MIN = 15 # Minutes for a long break (NOT YET IMPLEMENTED)
|
|
|
|
INTERRUPTED_MIN = 1 # Minimum minutes to ask to record an interrupted Pomodoro
|
2020-10-23 16:38:52 +00:00
|
|
|
QUIET = True
|
2020-07-05 16:55:18 +00:00
|
|
|
|
2020-07-05 17:26:38 +00:00
|
|
|
def pomodoro_prompt_plan(whatdid = ''):
|
2020-10-10 04:13:16 +00:00
|
|
|
""" Ask about what task will be worked on (showing last thing worked on)
|
|
|
|
"""
|
2020-11-08 02:13:06 +00:00
|
|
|
text = "What're you gonna do?"
|
2021-05-02 20:41:23 +00:00
|
|
|
# NOTE: If we can do system notifications that when clicked will focus the terminal
|
|
|
|
# running pomodoro prompt, that might be even better than the pop-up GUI text entry.
|
2021-05-02 17:01:42 +00:00
|
|
|
whatnext = prompt_window.prompt(prompt=text, task=whatdid)
|
2020-07-05 23:05:40 +00:00
|
|
|
# Pressing cancel returns None, but we want to just treat it as an empty string.
|
|
|
|
if whatnext is None:
|
|
|
|
whatnext = ''
|
2020-07-05 20:00:19 +00:00
|
|
|
return whatnext
|
2020-07-05 17:26:38 +00:00
|
|
|
|
|
|
|
def pomodoro_prompt_report(whatnext):
|
2020-10-10 04:13:16 +00:00
|
|
|
""" Ask what task was completed, showing the last task claimed to be being worked on
|
|
|
|
"""
|
2020-11-08 02:13:06 +00:00
|
|
|
text = "What'd you do?"
|
2021-05-02 20:31:36 +00:00
|
|
|
whatdid = prompt_window.prompt(prompt=text, task=whatnext)
|
2020-07-05 23:05:40 +00:00
|
|
|
# Pressing cancel returns None, but we want to just treat it as an empty string.
|
|
|
|
if whatdid is None:
|
|
|
|
whatdid = ''
|
2020-07-05 20:00:19 +00:00
|
|
|
return whatdid
|
2020-07-05 16:55:18 +00:00
|
|
|
|
2020-07-06 01:45:53 +00:00
|
|
|
def quit_prompt():
|
2020-10-10 04:13:16 +00:00
|
|
|
""" Confirm exit or start a new pomodoro
|
|
|
|
"""
|
2020-07-06 01:45:53 +00:00
|
|
|
print('\nPress p to start a new pomodoro, q to quit')
|
2020-10-10 04:13:16 +00:00
|
|
|
choice = input('p/q: ').strip().lower()
|
|
|
|
if choice in ('p', 'pomodoro'):
|
2020-07-06 01:45:53 +00:00
|
|
|
return
|
2020-10-10 04:13:16 +00:00
|
|
|
elif choice in ('q', 'quit', 'exit'):
|
|
|
|
sys.exit(0)
|
2020-07-06 01:45:53 +00:00
|
|
|
else:
|
|
|
|
quit_prompt()
|
|
|
|
|
2020-07-15 14:09:58 +00:00
|
|
|
def log_step(text, utc_start, end_section=False):
|
2020-10-10 04:13:16 +00:00
|
|
|
""" Write detailed log of all events
|
|
|
|
"""
|
2021-05-02 20:57:06 +00:00
|
|
|
timelog_file = Path(timelog_path(ext='log', daily=True))
|
2020-07-06 03:40:12 +00:00
|
|
|
timelog_file.touch(exist_ok=True)
|
|
|
|
with timelog_file.open('a') as timelog:
|
2020-07-06 12:48:24 +00:00
|
|
|
timelog.write(text + '\n')
|
2020-07-06 12:43:56 +00:00
|
|
|
if end_section:
|
2020-07-06 12:48:24 +00:00
|
|
|
timelog.write('\n\n')
|
2020-07-06 03:40:12 +00:00
|
|
|
|
2020-07-06 01:45:53 +00:00
|
|
|
|
2021-05-02 16:16:52 +00:00
|
|
|
def record_task(whatnext, whatdid, start, end=None):
|
2020-10-10 04:13:16 +00:00
|
|
|
""" Record completed pomodoro to CSV
|
|
|
|
"""
|
2020-07-05 19:41:04 +00:00
|
|
|
if end is None:
|
|
|
|
end = datetime.now(pytz.utc)
|
2021-05-02 20:57:06 +00:00
|
|
|
with open(prepare_file(), 'a', newline='') as csvfile:
|
2021-05-02 16:16:52 +00:00
|
|
|
# TODO make first line started, recorded, description, intention
|
2020-07-05 19:41:04 +00:00
|
|
|
timewriter = csv.writer(csvfile)
|
2021-05-02 16:16:52 +00:00
|
|
|
timewriter.writerow([start, end, whatdid, whatnext])
|
2020-07-05 16:55:18 +00:00
|
|
|
|
2020-07-06 12:43:56 +00:00
|
|
|
def str_minutes(time_diff):
|
2020-10-10 04:14:22 +00:00
|
|
|
""" Return at least whole seconds from a time difference
|
|
|
|
"""
|
2020-07-06 12:43:56 +00:00
|
|
|
return str(time_diff).split('.')[0]
|
|
|
|
|
2020-10-10 04:14:22 +00:00
|
|
|
def countdown_to(until):
|
|
|
|
""" Display a timer counting down to until
|
|
|
|
"""
|
|
|
|
while datetime.now(pytz.utc) <= until:
|
|
|
|
to_go = until-datetime.now(pytz.utc)
|
|
|
|
print('\r', str_minutes(to_go), sep='', end='')
|
|
|
|
time.sleep(1)
|
2020-07-05 16:55:18 +00:00
|
|
|
|
2021-05-02 20:57:06 +00:00
|
|
|
def prepare_file(ext='csv', daily=False, utc_start):
|
|
|
|
# Ensure log directory exists.
|
|
|
|
if not os.path.exists('log'):
|
|
|
|
os.makedirs('log')
|
|
|
|
# We consider 3am the switchover to a new day, so we skip extra math
|
|
|
|
# and use Pacific time to get three hours later than our Eastern time.
|
|
|
|
logpath_tz = timezone('US/Pacific')
|
|
|
|
logpath_date = utc_start.astimezone(logpath_tz)
|
|
|
|
timelog_path = 'log/' + str(logpath_date.year);
|
|
|
|
if (daily):
|
|
|
|
timelog_path += '-' \
|
|
|
|
+ format(logpath_date.month, '02') + '-' \
|
|
|
|
+ format(logpath_date.day, '02') + '.log'
|
|
|
|
timelog_path += '.' + ext
|
|
|
|
return timelog_path
|
|
|
|
|
2020-07-05 16:55:18 +00:00
|
|
|
def main():
|
2020-07-05 23:22:29 +00:00
|
|
|
whatdid = ''
|
|
|
|
while True:
|
|
|
|
whatnext = pomodoro_prompt_plan(whatdid)
|
2020-07-05 17:26:38 +00:00
|
|
|
|
2020-07-05 23:22:29 +00:00
|
|
|
start = datetime.now(pytz.utc)
|
2020-07-06 13:27:00 +00:00
|
|
|
end = start + timedelta(minutes=WORK_MIN)
|
2020-07-06 12:43:56 +00:00
|
|
|
log_step('Start with plan: ' + whatnext, start)
|
2020-07-05 23:22:29 +00:00
|
|
|
try:
|
2020-07-06 13:27:34 +00:00
|
|
|
print('Working on: ', whatnext)
|
2020-10-10 04:14:22 +00:00
|
|
|
countdown_to(end)
|
2020-10-10 04:20:18 +00:00
|
|
|
if not QUIET:
|
|
|
|
playsound('res/timesup.aac')
|
2020-07-05 19:41:04 +00:00
|
|
|
whatdid = pomodoro_prompt_report(whatnext)
|
2021-05-02 16:16:52 +00:00
|
|
|
record_task(whatnext, whatdid, start)
|
2020-07-06 12:43:56 +00:00
|
|
|
log_step('Completed pomodoro: ' + whatdid, start, True)
|
2020-07-06 13:40:08 +00:00
|
|
|
print('Worked on: ' + whatdid)
|
|
|
|
|
|
|
|
print('Break time!')
|
|
|
|
# We're taking a break. Clear out task start/end times.
|
|
|
|
start = None
|
2020-07-06 13:28:57 +00:00
|
|
|
end = datetime.now(pytz.utc) + timedelta(minutes=SHORT_MIN)
|
2020-10-10 04:14:22 +00:00
|
|
|
countdown_to(end)
|
2020-10-10 04:20:18 +00:00
|
|
|
if not QUIET:
|
|
|
|
playsound('res/timesup.aac')
|
2020-07-05 23:22:29 +00:00
|
|
|
continue
|
|
|
|
except KeyboardInterrupt:
|
2020-07-06 13:40:08 +00:00
|
|
|
if start is None:
|
|
|
|
quit_prompt()
|
|
|
|
continue
|
2020-07-06 13:29:28 +00:00
|
|
|
time_spent = datetime.now(pytz.utc) - start
|
2020-07-06 13:40:08 +00:00
|
|
|
if time_spent < timedelta(minutes=INTERRUPTED_MIN):
|
|
|
|
quit_prompt()
|
|
|
|
continue
|
|
|
|
time_spent_str = str_minutes(time_spent)
|
|
|
|
print('\n{} time spent, save?'.format(time_spent_str))
|
|
|
|
choice = input('[y]/n: ').strip()
|
|
|
|
if choice.lower() in ('y', 'yes', ''):
|
|
|
|
whatdid = pomodoro_prompt_report(whatnext)
|
2021-05-02 16:16:52 +00:00
|
|
|
record_task(whatnext, whatdid, start)
|
2020-07-06 13:40:08 +00:00
|
|
|
log_step('Incomplete (' + time_spent_str + ') pomodoro: ' + whatdid, start, True)
|
2020-07-06 12:52:08 +00:00
|
|
|
quit_prompt()
|
2020-07-05 23:22:29 +00:00
|
|
|
else:
|
|
|
|
print('What did you break?')
|
|
|
|
# If we somehow end up here, try a last-ditch effort to save.
|
2021-05-02 16:16:52 +00:00
|
|
|
record_task(whatnext, 'Incomplete, interrupted task:' + whatnext, start)
|
2020-07-06 12:43:56 +00:00
|
|
|
log_step('Incomplete, interrupted task:' + whatnext, start, True)
|
2020-07-05 19:41:04 +00:00
|
|
|
|
|
|
|
|
2020-07-05 16:55:18 +00:00
|
|
|
if __name__ == '__main__':
|
|
|
|
main()
|