Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save agusmakmun/0555149739b40fc671738adfaba048f6 to your computer and use it in GitHub Desktop.
Save agusmakmun/0555149739b40fc671738adfaba048f6 to your computer and use it in GitHub Desktop.
Go Frendi's pomodoro, kanban, and reminder application. Work on terminal, written by using Python
import os, json, time, datetime, math, curses
#global variables
KANBAN_FILE = os.path.expanduser("~/.kanban.json")
DEFAULT_KANBAN = {
"tasks" : [],
"boards" : ["To do", "Doing", "Done"],
"work_time" : 25 * 60,
"rest_time" : 5 * 60
}
COUNTER = DEFAULT_KANBAN["work_time"]
def str_to_timestamp(string):
return time.mktime(datetime.datetime.strptime(string, "%Y-%m-%d %H:%M:%S").timetuple())
def timestamp_to_str(timestamp):
time_tuple = time.localtime(timestamp)
return time.strftime("%Y-%m-%d %H:%M:%S", time_tuple)
def menu():
print("(p) Pomodoro (a) Add Task (d) Delete Task")
print("(c) Configuration (e) Edit Task (q) Quit Program")
def load_kanban():
if not os.path.exists(KANBAN_FILE):
return DEFAULT_KANBAN
else:
with open(KANBAN_FILE, "r") as infile:
return json.load(infile)
def save_kanban(kanban):
with open(KANBAN_FILE, "w+") as outfile:
json.dump(kanban, outfile)
def change_configuration(kanban):
# prompt and save work time
work_time = raw_input("Work time in minutes (previous value: %d) : " % (int(kanban["work_time"])/60))
if work_time.strip().isdigit():
work_time = int(work_time)
kanban["work_time"] = work_time * 60
# prompt and save rest time
rest_time = raw_input("Rest time in minutes (previous value: %d) : " % (int(kanban["rest_time"])/60))
if rest_time.strip().isdigit():
rest_time = int(rest_time)
kanban["rest_time"] = rest_time * 60
# prompt and save boards
boards = raw_input("Boards, separated by '|' (previous value: '%s') : " % (" | ".join(kanban["boards"])))
if boards.strip() != "":
boards = boards.split("|")
for i,board in enumerate(boards):
boards[i] = board.strip()
kanban["boards"] = boards
# return the modified kanban
return kanban
def get_available_board_option(kanban):
boards = list(kanban["boards"])
for i, board in enumerate(boards):
boards[i] = "'" + board + "'"
return ", ".join(kanban["boards"])
def normalize_board(board_name, kanban):
boards = kanban["boards"]
for board in boards:
if board.replace(" ", "").lower() == board_name.replace(" ", "").lower():
return board
return board_name.strip()
def add_task(kanban):
# task name
task_name = raw_input("Task name : ")
# board
default_board = kanban["boards"][0] if len(kanban)>0 else ""
board = raw_input("Board name (available : %s), (default : '%s') : " %(get_available_board_option(kanban), default_board))
board = normalize_board(board, kanban)
if board not in kanban["boards"]:
kanban["boards"].append(board)
# remind_on
remind_on = raw_input("Remind on (format : Y-m-d H:M:S), (default : '') : ")
# remind_until
remind_until = raw_input("Remind until (format : Y-m-d H:M:S), (default : '') : ")
if remind_until == "" and remind_on != "":
remind_until = timestamp_to_str(str_to_timestamp(remind_on)+(60*30)) # default remind until 30 minutes
# add to kanban
new_task = {
"name" : task_name,
"board" : board,
"remind_on" : remind_on,
"remind_until" : remind_until
}
kanban["tasks"].append(new_task)
return kanban
def delete_task(kanban):
task_id = raw_input("Task id : ")
if task_id.isdigit():
kanban["tasks"].pop(int(task_id)-1)
return kanban
def edit_task(kanban):
task_id = raw_input("Task id : ")
if task_id.isdigit():
task_id = int(task_id) - 1
task = kanban["tasks"][task_id]
# task name
default_task_name = task["name"]
task_name = raw_input("Task name (previous value : %s) : " % (default_task_name))
if task_name.strip() != "":
task["name"] = task_name
# board
default_board = task["board"]
board = raw_input("Board name (available : %s), (previous value : '%s') : " %(get_available_board_option(kanban), default_board))
board = normalize_board(board, kanban)
if board.strip() != "":
if board not in kanban["boards"]:
kanban["boards"].append(board)
task["board"] = board
# remind_on
default_remind_on = task["remind_on"]
remind_on = raw_input("Remind on (format : yyyy-mm-dd H:i:s), (previous value : '%s') : " % (default_remind_on))
if remind_on.strip() != "":
task["remind_on"] = remind_on
elif default_remind_on.strip() != "":
keep_value = raw_input("Do you want to keep the previous value '%s' (y/n)" % (default_remind_on))
if keep_value.lower() == "n":
task["remind_on"] = ""
# remind_until
if task["remind_on"].strip() == "":
task["remind_until"] = ""
else:
default_remind_until = task["remind_until"]
remind_until = raw_input("Remind until (format : yyyy-mm-dd H:i:s), (previous value : '%s') : " % (default_remind_until))
if remind_until.strip() != "":
task["remind_until"] = remind_until
return kanban
def format_seconds(seconds):
m, s = divmod(seconds, 60)
h, m = divmod(m, 60)
return "%d:%02d:%02d" % (h, m, s)
def kanban_display(kanban):
boards = kanban["boards"]
tasks = kanban["tasks"]
board_section = {}
board_width = {}
max_card_count = 0
reminded_tasks = []
current_time = time.time()
for i, task in enumerate(tasks):
if task["remind_on"].strip() != "" and task["remind_until"].strip() != "":
time_start = str_to_timestamp(task["remind_on"])
time_stop = str_to_timestamp(task["remind_until"])
if time_start < current_time and time_stop > current_time:
reminded_tasks.append(str(i+1) + " . " + task["name"])
# get board_width and board_section
for board in boards:
# get board_task
board_task = []
for i, card in enumerate(tasks):
if card["board"] == board:
card["id"] = str(i+1)
board_task.append(card)
# get max_card_count
if len(board_task) > max_card_count:
max_card_count = len(board_task)
# get max_width
if len(board_task) == 0:
max_width = len(board)
else:
max_width = max(len(board),23) # 23 is count of character for yyyy-mm-dd H:i:s
for i, card in enumerate(board_task):
width = len(card["id"]) + 3 + len(card["name"])
if width > max_width:
max_width = width
board_section[board] = board_task
board_width[board] = max_width
# prepare kanban
first_line = []
card_title_list = []
card_remind_on_list = []
card_remind_until_list = []
for board in boards:
width = board_width[board]
board_title = board.ljust(width, " ")
first_line.append(board_title)
for i in range(max_card_count):
card_title = []
card_remind_on = []
card_remind_until = []
for board in boards:
width = board_width[board]
if i>=len(board_section[board]):
card_title.append("".ljust(width, " "))
card_remind_on.append("".rjust(width, " "))
card_remind_until.append("".rjust(width, " "))
else:
card = board_section[board][i]
title = card["id"] + " . " + card["name"]
card_title.append(title.ljust(width, " "))
card_remind_on.append(card["remind_on"].rjust(width, " "))
card_remind_until.append(card["remind_until"].rjust(width, " "))
card_title_list.append(card_title)
card_remind_on_list.append(card_remind_on)
card_remind_until_list.append(card_remind_until)
# draw kanban
display = ""
if len(reminded_tasks) > 0:
display += "Reminder :\n"
for task in reminded_tasks:
display += "* %s\n" %(task)
if len(reminded_tasks) > 0:
display += "\n\n"
# the kanban
first_line = " | ".join(first_line)
display += first_line + "\n" # board title
display += "=" * len(first_line) + "\n" # the separator
for i in range(max_card_count):
card_title = " | ".join(card_title_list[i])
card_remind_on = " | ".join(card_remind_on_list[i])
card_remind_until = " | ".join(card_remind_until_list[i])
display += card_title + "\n"
display += card_remind_on + "\n"
display += card_remind_until + "\n"
display += "-" * len(first_line) + "\n" # the separator
return display
def display_pomodoro(stdscr, counter, is_working, is_pause, kanban):
formatted_counter = format_seconds(counter)
counter_status = "PAUSED" if is_pause else " "
working_status = "WORKING" if is_working else "REST "
command_bar_1 = "(w) Work Mode (r) Rest Mode (space) Pause/Resume"
command_bar_2 = " (x) Exit Pomodoro (q) Quit Program"
# clear the screen and redraw
try:
stdscr.addstr(0,0, "%s | %s %s" %(working_status, formatted_counter, counter_status), curses.A_BOLD)
stdscr.addstr(1,0, command_bar_1)
stdscr.addstr(2,0, command_bar_2)
stdscr.addstr(4,0, kanban_display(kanban))
stdscr.refresh()
except curses.error:
pass
def pomodoro(kanban):
quit = False
# some variables
is_working = True
is_pause = False
counter = int(kanban["work_time"])
initial_time = math.ceil(time.time())
# create stdscr object, make getch non-blocking
stdscr = curses.initscr()
stdscr.nodelay(1)
# turn off echo, hide cursor
curses.noecho()
curses.curs_set(0)
while True:
# the process: calculate counter
current_time = math.floor(time.time())
if current_time - initial_time >=1:
initial_time = current_time
if not is_pause: # if is_pause, don't reduce the counter
stdscr.clear()
counter -= 1
# the display
display_pomodoro(stdscr, counter, is_working, is_pause, kanban)
# the process: calculate is_working
if counter <= 0:
counter = int(kanban["rest_time"]) if is_working else int(kanban["work_time"])
curses.beep()
# the command
command = stdscr.getch()
if command == ord("x") or command == ord("X"): # exit
break
elif command == ord(" "): # pause/resume
is_pause = not is_pause
elif not is_working and (command == ord("w") or command == ord("W")): # work
is_working = True
counter = int(kanban["work_time"])
curses.beep()
elif is_working and (command == ord("r") or command == ord("R")): # resume
is_working = False
counter = int(kanban["rest_time"])
curses.beep()
elif command == ord("q") or command == ord("Q"): #quit
quit = True
break
curses.endwin()
return quit
if __name__ == "__main__":
# load kanban
kanban = load_kanban()
choice = "p"
while True:
if choice == "p" or choice == "P": # user choose "pomodoro & kanban"
quit = pomodoro(kanban)
if quit:
break
elif choice == "c" or choice == "C": # user choose "configuration"
kanban = change_configuration(kanban)
elif choice == "a" or choice == "A": # user choose "add task"
kanban = add_task(kanban)
elif choice == "e" or choice == "E": # user choose "edit task"
kanban = edit_task(kanban)
elif choice == "d" or choice == "D": # user choose "delete task"
kanban = delete_task(kanban)
elif choice == "q" or choice == "Q": # user choose "exit"
break
else:
print("Invalid command")
# save kanban and show pomodoro
save_kanban(kanban)
print("\n"*100) # hack to clear the screen
print(kanban_display(kanban))
menu() # show the menu and read the user"s choice
choice = raw_input("Your choice : ")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment