ezhik.

TaskFlow

Open source
GitHub ↗ Скачать ZIP
taskflow.py8.1 KB · pythonraw
#!/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()