#!/usr/bin/env python3 """TaskFlow — terminal task manager with priorities and categories.""" import argparse import json import os import sys from datetime import datetime class C: RED = "\033[91m" GREEN = "\033[92m" YELLOW = "\033[93m" BLUE = "\033[94m" MAGENTA = "\033[95m" CYAN = "\033[96m" WHITE = "\033[97m" GRAY = "\033[90m" BOLD = "\033[1m" DIM = "\033[2m" STRIKE = "\033[9m" RESET = "\033[0m" BANNER = f""" {C.CYAN}{C.BOLD} ████████╗ █████╗ ███████╗██╗ ██╗███████╗██╗ ██████╗ ██╗ ██╗ ╚══██╔══╝██╔══██╗██╔════╝██║ ██╔╝██╔════╝██║ ██╔═══██╗██║ ██║ ██║ ███████║███████╗█████╔╝ █████╗ ██║ ██║ ██║██║ █╗ ██║ ██║ ██╔══██║╚════██║██╔═██╗ ██╔══╝ ██║ ██║ ██║██║███╗██║ ██║ ██║ ██║███████║██║ ██╗██║ ███████╗╚██████╔╝╚███╔███╔╝ ╚═╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚═╝ ╚══════╝ ╚═════╝ ╚══╝╚══╝{C.RESET} {C.GRAY} ────────────────────────────────────────────────────────────────{C.RESET} {C.DIM} Terminal Task Manager v1.0 {C.GRAY}by mainstarkov{C.RESET} {C.GRAY} ────────────────────────────────────────────────────────────────{C.RESET} """ PRIORITY_COLORS = {"high": C.RED, "medium": C.YELLOW, "low": C.GREEN} PRIORITY_ICONS = {"high": "🔴", "medium": "🟡", "low": "🟢"} DATA_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "tasks.json") def load_tasks() -> list[dict]: if os.path.exists(DATA_FILE): with open(DATA_FILE, "r") as f: return json.load(f) return [] def save_tasks(tasks: list[dict]): with open(DATA_FILE, "w") as f: json.dump(tasks, f, indent=2, ensure_ascii=False) def next_id(tasks: list[dict]) -> int: return max((t["id"] for t in tasks), default=0) + 1 def cmd_add(args): tasks = load_tasks() task = { "id": next_id(tasks), "title": " ".join(args.title), "priority": args.priority, "category": args.category or "general", "done": False, "created": datetime.now().isoformat(), "completed": None, } tasks.append(task) save_tasks(tasks) pc = PRIORITY_COLORS[task["priority"]] pi = PRIORITY_ICONS[task["priority"]] print(f"\n {C.GREEN}✓{C.RESET} Task #{task['id']} added: {C.BOLD}{task['title']}{C.RESET}") print(f" {pi} {pc}{task['priority']}{C.RESET} 📂 {task['category']}\n") def cmd_done(args): tasks = load_tasks() found = [t for t in tasks if t["id"] == args.id] if not found: print(f"\n {C.RED}✗ Task #{args.id} not found{C.RESET}\n") return found[0]["done"] = True found[0]["completed"] = datetime.now().isoformat() save_tasks(tasks) print(f"\n {C.GREEN}✓{C.RESET} Task #{args.id} completed: {C.STRIKE}{found[0]['title']}{C.RESET}\n") def cmd_remove(args): tasks = load_tasks() before = len(tasks) tasks = [t for t in tasks if t["id"] != args.id] if len(tasks) == before: print(f"\n {C.RED}✗ Task #{args.id} not found{C.RESET}\n") return save_tasks(tasks) print(f"\n {C.GREEN}✓{C.RESET} Task #{args.id} removed\n") def cmd_list(args): tasks = load_tasks() if args.category: tasks = [t for t in tasks if t.get("category") == args.category] if args.priority: tasks = [t for t in tasks if t.get("priority") == args.priority] if not args.all: tasks = [t for t in tasks if not t["done"]] if not tasks: print(f"\n {C.GRAY}No tasks found.{C.RESET}\n") return priority_order = {"high": 0, "medium": 1, "low": 2} tasks.sort(key=lambda t: (t["done"], priority_order.get(t.get("priority", "low"), 9))) categories = sorted(set(t.get("category", "general") for t in tasks)) w = 64 print(f"\n{C.CYAN} ╔{'═' * w}╗{C.RESET}") total = len(tasks) done = sum(1 for t in tasks if t["done"]) header = f"Tasks: {total} total, {done} done, {total - done} pending" pad = max(0, w - 2 - len(header)) print(f"{C.CYAN} ║ {C.BOLD}{header}{C.RESET}{' ' * pad}{C.CYAN}║{C.RESET}") print(f"{C.CYAN} ╠{'═' * w}╣{C.RESET}") for cat in categories: cat_tasks = [t for t in tasks if t.get("category", "general") == cat] print(f"{C.CYAN} ║{C.RESET} {C.MAGENTA}📂 {cat.upper()}{C.RESET}{' ' * max(0, w - 5 - len(cat))}{C.CYAN}║{C.RESET}") print(f"{C.CYAN} ╟{'─' * w}╢{C.RESET}") for t in cat_tasks: tid = t["id"] title = t["title"] pri = t.get("priority", "low") pi = PRIORITY_ICONS.get(pri, "⚪") pc = PRIORITY_COLORS.get(pri, C.WHITE) if t["done"]: check = f"{C.GREEN}✔{C.RESET}" title_fmt = f"{C.GRAY}{C.STRIKE}{title}{C.RESET}" else: check = f"{C.GRAY}○{C.RESET}" title_fmt = f"{C.WHITE}{title}{C.RESET}" line = f" {check} {C.GRAY}#{tid:<4}{C.RESET} {pi} {title_fmt}" visible = 6 + len(str(tid)) + 3 + len(title) + 2 pad = max(0, w - visible) pri_tag = f"{pc}{pri}{C.RESET}" print(f"{C.CYAN} ║{C.RESET}{line}{' ' * pad} {pri_tag} {C.CYAN}║{C.RESET}") print(f"{C.CYAN} ╟{'─' * w}╢{C.RESET}") # Progress bar if total > 0: pct = done / total bar_w = 30 filled = int(pct * bar_w) bar = f"{C.GREEN}{'█' * filled}{C.GRAY}{'░' * (bar_w - filled)}{C.RESET}" pct_str = f"{pct * 100:.0f}%" pad = max(0, w - 2 - bar_w - 2 - len(pct_str) - 12) print(f"{C.CYAN} ║{C.RESET} Progress: {bar} {C.BOLD}{pct_str}{C.RESET}{' ' * pad}{C.CYAN}║{C.RESET}") print(f"{C.CYAN} ╚{'═' * w}╝{C.RESET}\n") def cmd_clear(args): tasks = load_tasks() remaining = [t for t in tasks if not t["done"]] removed = len(tasks) - len(remaining) save_tasks(remaining) print(f"\n {C.GREEN}✓{C.RESET} Cleared {removed} completed tasks\n") def main(): parser = argparse.ArgumentParser(prog="taskflow", description="Terminal task manager") sub = parser.add_subparsers(dest="command") p_add = sub.add_parser("add", help="add a new task") p_add.add_argument("title", nargs="+", help="task title") p_add.add_argument("-p", "--priority", choices=["high", "medium", "low"], default="medium") p_add.add_argument("-c", "--category", help="task category (default: general)") p_done = sub.add_parser("done", help="mark task as completed") p_done.add_argument("id", type=int, help="task ID") p_rm.add_argument("id", type=int, help="task ID") p_list = sub.add_parser("list", help="show tasks") p_list.add_argument("-c", "--category", help="filter by category") p_list.add_argument("-p", "--priority", choices=["high", "medium", "low"]) p_list.add_argument("-a", "--all", action="store_true", help="include completed tasks") sub.add_parser("clear", help="remove all completed tasks") args = parser.parse_args() print(BANNER) commands = { "add": cmd_add, "done": cmd_done, "remove": cmd_remove, "list": cmd_list, "clear": cmd_clear, } if args.command in commands: commands[args.command](args) else: parser.print_help() if __name__ == "__main__": main()