Week planner
Written by: Gyula Rabai
WeekPlanner is a simple, terminal-based task management and pomodoro-style productivity tool written in Python. It helps you manage tasks with importance levels, tracks progress, and uses an efficiency-based work/break ratio to structure your time.
Download: weekplanner.py
Features
Task management (add, list, select)Progress tracking (based on importance-weighted completion)
Adaptive Pomodoro timer (work/break times based on efficiency)
Break time accumulator
Colorful terminal output
Installation & Requirements
Python 3.xWindows (uses
winsound for beeps)No external libraries required
Save the script as weekplanner.py and run it:
python weekplanner.py
Tasks are stored in a file named tasks.txt in the same directory as the script.
Task File Format (tasks.txt)
Each line represents one task:
Task name with spaces importance_value
Study for exam 3.5
Clean the kitchen 1.0
Write report 4.2
Importance is a positive float number (higher = more important).
Commands Overview
| Command | Description | Example |
|---|---|---|
| a / add | Add a new task | a Write blog post 2.5 |
| ls | List all tasks and refresh internal lists | ls |
| d taskname | Start working on a specific task | d Write blog post |
| dn | Do next task (one with least progress, random if tie) | dn |
| n | Show next task (without starting) | n |
| e value | Set efficiency (0–1) or ratio (e.g. 1/2) | e 0.8 or e 4/5 |
| ge | Get current efficiency | ge |
| s [minutes] | Start/restart timer (default: calculated work time) | s or s 25 |
| p | Pause timer | p |
| r | Reset timer | r |
| tl | Show time left | tl |
| b [minutes] | Start break (default: accumulated break time) | b or b 10 |
| sb minutes | Set accumulated break time | sb 15 |
| gb | Get current accumulated break time | gb |
| gwb | Get default work & break times based on efficiency | gwb |
| gp | Show progress for all tasks | gp |
| c | Clear screen | c |
| h | Show help menu | h |
| q | Quit the application | q |
How the Timer & Efficiency Work
The app uses an efficiency factor (default: 0.8) to calculate work/break ratios.
Formula:
Rest time = max(5, (1/efficiency - 1) × 20) minutesWork time = rest_time × (efficiency / (1 - efficiency)) minutes
Example with efficiency = 0.8:
Rest ≈ 5 minWork ≈ 20 min
Each time you finish a work session, the corresponding break time is added to your break bank.
Progress Tracking
Tasks with lower progress (and higher importance) are prioritized when using dn (do next).
Keyboard Shortcuts Summary
Enter: repeat last command (if empty input)q: quit
Source code
MainScript.py
import random
import time
import os
import threading as th
import winsound
# -----------------------------------
# Colors
# -----------------------------------
# Enable ANSI escape codes on Windows (for color support)
os.system('')
# Color constants
RESET = "\033[0m"
BOLD = "\033[1m"
RED = "\033[91m"
GREEN = "\033[92m"
YELLOW = "\033[93m"
BLUE = "\033[94m"
CYAN = "\033[96m"
MAGENTA = "\033[95m"
# -----------------------------------
# Time and Efficiency
# -----------------------------------
efficiency = 0.8
def calculate_work_break_times():
# Calculate rest time: max(5, (1 / efficiency - 1) * 20)
rest_time = max(5, (1 / efficiency - 1) * 20)
# Calculate work time: rest_time * (efficiency / (1 - efficiency))
work_time = rest_time * (efficiency / (1 - efficiency))
return work_time, rest_time
def getWorkTime():
"""
Get the work time based on the efficiency.
Returns:
float: The calculated work time in minutes.
"""
work_time, _ = calculate_work_break_times()
return work_time
def getBreakTime():
"""
Get the break time based on the efficiency.
Returns:
float: The calculated break time in minutes.
"""
_, break_time = calculate_work_break_times()
return break_time
# -----------------------------------
# Tasks File
# -----------------------------------
def get_task_name(line):
parts = line.split(" ")
res = parts[:-1]
return (" ".join(res)).strip()
def get_task_imp(line):
return float(line.split(" ")[-1])
taskFName = os.path.dirname(os.path.abspath(__file__))+ "/tasks.txt"
def GetFile():
if os.path.exists(taskFName):
task_f = open(taskFName, "r")
else:
task_f = open(taskFName, "w+")
return task_f
importances = dict()
progress = dict()
def get_task():
task_f = GetFile()
tasks = task_f.readlines()
task_f.close()
global importances
global progress
importances.clear()
n_progress = dict()
for task in tasks:
n_progress[get_task_name(task)] = progress[get_task_name(task)] if get_task_name(task) in progress else 0
importances[get_task_name(task)] = get_task_imp(task)
progress = n_progress
return tasks
def add_t(name, importance):
task_f = GetFile()
task_f.write(f"\n{name} {importance}")
task_f.close()
# -----------------------------------
# Timer File
# -----------------------------------
start_time = None
time_interval = 0
paused = False
sThread = None
break_time = 0
quit = False
def start(mins):
global start_time
global paused
global sThread
if paused:
paused = False
else:
global time_interval
time_interval = mins * 60
if start_time == None:
start_time = time.time()
sThread = th.Thread(target=timing_loop)
sThread.start()
def diff(tp):
if tp is None:
return 0
return time.time() - tp
def pause():
global paused
global time_interval
global start_time
global sThread
if paused:
print(f"{YELLOW}Timing is already paused.{RESET}")
return
if start_time == None:
print(f"{RED}Timing has not started yet.{RESET}")
return
time_interval -= diff(start_time)
start_time = None
paused = True
if sThread is not None:
sThread.join()
sThread = None
def timing_loop():
while not quit and start_time != None and not paused:
if diff(start_time) >= time_interval:
while not quit and start_time != None:
time.sleep(1)
winsound.Beep(1000, 1000)
print(f"\n{GREEN}Time is up!{RESET}\n{MAGENTA}> {RESET}", end="")
break
time.sleep(0.25)
def reset():
global start_time
global time_interval
global paused
global pause_start
global paused_time
global sThread
start_time = None
time_interval = 0
paused = False
if sThread is not None:
sThread.join()
sThread = None
# -----------------------------------
# Next Task
# -----------------------------------
def next_task():
tasks = get_min_keys(progress)
if len(tasks) == 0:
print(f"{RED}No tasks available.{RESET}")
return None
task = random.choice(tasks)
return task
def get_min_keys(d: dict[str, int]) -> list[str]:
if not d:
return []
min_value = min(d.values())
min_keys = [key for key, value in d.items() if value == min_value]
return min_keys
def do(task):
if task is None:
print(f"{RED}No task to do.{RESET}")
return
print(f"{GREEN}Doing task: {task} with importance {importances[task]}{RESET}")
wt, bt = calculate_work_break_times()
print(f"{YELLOW}Work time {wt:.1f} minutes, Break time {bt:.1f} minutes{RESET}")
if input(f"{BLUE}continue? (y/n): {RESET}").strip().lower() != 'n':
start(wt)
global break_time
break_time += bt
progress[task] += 1 / importances[task]
get_task()
print(f"{CYAN}h for help menu{RESET}")
ui = ""
while ui != "q":
ui = input(f"{MAGENTA}> {RESET}")
words = ui.strip().split()
if ui == "":
continue
cmd = words[0]
match cmd:
case "a":
try:
cmdLen = len(cmd) + 1
importanceLen = len(words[-1]) +1
name = ui[cmdLen:-importanceLen]
importance = words[-1]
add_t(name, importance)
except Exception as e:
print(f"{RED}{e.args} Incorrect format: a for add task e.g. add task 1, add [task name] [importance]{RESET}")
case "b":
try:
if len(words) > 1:
start(float(words[1]))
print(f"Break started for {words[1]} minutes")
break_time -= float(words[1])
else:
start(break_time)
break_time = 0
except Exception as e:
print(f"{RED}{e.args} Incorrect format: b for break e.g. b 5 for 5 min break{RESET}")
case "sb":
try:
if len(words) > 1:
break_time = float(words[1])
else:
_, bt = calculate_work_break_times()
break_time = bt
except Exception as e:
print(f"{RED}{e.args} Incorrect format: sb for set break time e.g. sb 5 for 5 min break{RESET}")
case "gb":
print(f"{CYAN}Break time is set to {break_time:.1f} minutes{RESET}")
case "gwb":
work_time, bt = calculate_work_break_times()
print(f"{CYAN}Default work time: {work_time:.1f} minutes, Break time: {bt:.1f} minutes{RESET}")
case "c":
os.system('cls')
print(f"{CYAN}h for help menu{RESET}")
case "d":
if len(words) < 2:
print(f"{RED}Please specify a task to do.{RESET}")
continue
task = " ".join(words[1:])
if task not in progress:
print(f"{RED}Task '{task}' not found.{RESET}")
continue
if paused:
print(f"{YELLOW}Timing is paused. Please resume before doing a task.{RESET}")
continue
do(task)
case "dn":
task = next_task()
do(task)
case "e":
try:
if "/" in words[1]:
num = words[1].split("/")
efficiency = float(num[0]) / float(num[1])
else:
efficiency = float(words[1])
except Exception as e:
print(f"{RED}{e.args} Incorrect format: e for set efficiency e.g. e 0.5 or e 1/2{RESET}")
case "ge":
print(f"{CYAN}Efficiency is set to {efficiency}{RESET}")
case "ls":
task = get_task()
print(f"{BOLD}{BLUE}Tasks:{RESET}")
for line in task:
print(f"{BLUE}{line.strip()}{RESET}")
case "h":
print(f"{BOLD}{CYAN}a for add task e.g. add task 1, add [task name] [importance]{RESET}")
print(f"{CYAN}b for break e.g. b 5 for 5 min break{RESET}")
print(f"{CYAN}sb for set break time e.g. sb 5 for 5 min break{RESET}")
print(f"{CYAN}gb for get break time{RESET}")
print(f"{CYAN}gwb for get default work and break time{RESET}")
print(f"{CYAN}c for clear{RESET}")
print(f"{CYAN}d for do task e.g. d task1{RESET}")
print(f"{CYAN}dn for do next task with minimum progress{RESET}")
print(f"{CYAN}e for set efficiency e.g. e 0.5 or e 1/2{RESET}")
print(f"{CYAN}ge for get efficiency{RESET}")
print(f"{CYAN}ls for list of tasks and their importance (also refreshes internal task lists){RESET}")
print(f"{CYAN}h for help menu{RESET}")
print(f"{CYAN}n for next task{RESET}")
print(f"{CYAN}p for pause timing{RESET}")
print(f"{CYAN}gp for get progress{RESET}")
print(f"{CYAN}q for quit{RESET}")
print(f"{CYAN}r for reset timing{RESET}")
print(f"{CYAN}tl for time left{RESET}")
print(f"{CYAN}s for start or restart timing e.g. s 5 for 5 min work time default to work time calculated by efficiency{RESET}")
case "n":
task = next_task()
print(f"{GREEN}Next task: '{task}' with importance {importances[task]}{RESET}")
case "p":
pause()
case "gp":
print(f"{CYAN}Progress:{RESET}")
for task, prog in progress.items():
print(f"{GREEN} - {task}: {prog:.1f}{RESET}")
case "q":
quit = True
break
case "r":
reset()
case "tl":
if paused:
print(f"{CYAN}Time left: {int(time_interval // 60)} min {time_interval % 60:.1f} seconds{RESET}")
continue
d = diff(start_time)
sec = time_interval - d
if sec < 0:
sec = 0
print(f"{CYAN}Time left: {int(sec // 60)} min {sec % 60:.1f} seconds{RESET}")
case "s":
try:
if len(words) > 1:
start(float(words[1]))
break_time += float(words[1]) * (1-efficiency) / efficiency
else:
if not paused:
break_time += calculate_work_break_times()[1]
start(calculate_work_break_times()[0])
except Exception as e:
print(f"{RED}{e.args} Incorrect format: s for start or restart timing e.g. s 5 for 5 min work time{RESET}")