Add 'taskdeps' script
							parent
							
								
									41c7740c0c
								
							
						
					
					
						commit
						fc08e2046b
					
				| 
						 | 
				
			
			@ -0,0 +1,144 @@
 | 
			
		|||
#!/usr/bin/python
 | 
			
		||||
 | 
			
		||||
import argparse
 | 
			
		||||
import json
 | 
			
		||||
import subprocess
 | 
			
		||||
from collections import defaultdict
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_task_data():
 | 
			
		||||
    command = (
 | 
			
		||||
        "task +PENDING or +WAITING -COMPLETED -DELETED export | "
 | 
			
		||||
        "jq '[.[] | {uuid: .uuid, id, depends: .depends, description: .description, status: .status }]'"
 | 
			
		||||
    )
 | 
			
		||||
    output = subprocess.check_output(command, shell=True)
 | 
			
		||||
    return json.loads(output)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def parse_task_data(data):
 | 
			
		||||
    dependency_graph = defaultdict(list)
 | 
			
		||||
    task_details = {}
 | 
			
		||||
    dependent_tasks = set()
 | 
			
		||||
 | 
			
		||||
    for task in data:
 | 
			
		||||
        task_id = task["uuid"]
 | 
			
		||||
        task_details[task_id] = {
 | 
			
		||||
            "id": task.get("id", "?"),
 | 
			
		||||
            "description": task.get("description", "No description"),
 | 
			
		||||
            "status": task.get("status", "Unknown status"),
 | 
			
		||||
        }
 | 
			
		||||
        if task["depends"]:
 | 
			
		||||
            for dependency in task["depends"]:
 | 
			
		||||
                dependency_graph[dependency].append(task_id)
 | 
			
		||||
                dependent_tasks.add(task_id)
 | 
			
		||||
 | 
			
		||||
    root_tasks = set(task_details.keys()) - dependent_tasks
 | 
			
		||||
    return task_details, dependency_graph, root_tasks
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_all_parents(task_id, dependency_graph):
 | 
			
		||||
    return [
 | 
			
		||||
        parent for parent, children in dependency_graph.items() if task_id in children
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def build_ascii_dag(
 | 
			
		||||
    task_id,
 | 
			
		||||
    task_details,
 | 
			
		||||
    dependency_graph,
 | 
			
		||||
    prefix="",
 | 
			
		||||
    is_last=True,
 | 
			
		||||
    show_id=True,
 | 
			
		||||
    visited=None,
 | 
			
		||||
):
 | 
			
		||||
    if visited is None:
 | 
			
		||||
        visited = set()
 | 
			
		||||
 | 
			
		||||
    if task_id in visited:
 | 
			
		||||
        return [f"{prefix}{'└── ' if is_last else '├── '}... (cycle detected)"]
 | 
			
		||||
 | 
			
		||||
    visited.add(task_id)
 | 
			
		||||
 | 
			
		||||
    task_info = task_details[task_id]
 | 
			
		||||
    task_line = f"{prefix}{'└── ' if is_last else '├── '}{task_info['id'] + ': ' if show_id else ''}{task_info['description']} ({task_info['status']})"
 | 
			
		||||
    lines = [task_line]
 | 
			
		||||
 | 
			
		||||
    children = dependency_graph.get(task_id, [])
 | 
			
		||||
    for idx, child in enumerate(children):
 | 
			
		||||
        child_is_last = idx == len(children) - 1
 | 
			
		||||
        child_prefix = prefix + ("    " if is_last else "│   ")
 | 
			
		||||
        lines.extend(
 | 
			
		||||
            build_ascii_dag(
 | 
			
		||||
                child,
 | 
			
		||||
                task_details,
 | 
			
		||||
                dependency_graph,
 | 
			
		||||
                child_prefix,
 | 
			
		||||
                child_is_last,
 | 
			
		||||
                show_id,
 | 
			
		||||
                visited.copy(),
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    return lines
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def render_dependency_dag(task_details, dependency_graph, root_tasks, show_id):
 | 
			
		||||
    dag_lines = []
 | 
			
		||||
    global_visited = set()
 | 
			
		||||
 | 
			
		||||
    def dfs(task_id, prefix="", is_last=True, visited=None):
 | 
			
		||||
        if visited is None:
 | 
			
		||||
            visited = set()
 | 
			
		||||
 | 
			
		||||
        if task_id in visited:
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        visited.add(task_id)
 | 
			
		||||
        global_visited.add(task_id)
 | 
			
		||||
 | 
			
		||||
        task_info = task_details[task_id]
 | 
			
		||||
        task_line = f"{prefix}{'└── ' if is_last else '├── '}{str(task_info['id']) + ': ' if show_id else ''}{task_info['description']} ({task_info['status']})"
 | 
			
		||||
        dag_lines.append(task_line)
 | 
			
		||||
 | 
			
		||||
        children = dependency_graph.get(task_id, [])
 | 
			
		||||
        for idx, child in enumerate(children):
 | 
			
		||||
            child_is_last = idx == len(children) - 1
 | 
			
		||||
            child_prefix = prefix + ("    " if is_last else "│   ")
 | 
			
		||||
            dfs(child, child_prefix, child_is_last, visited.copy())
 | 
			
		||||
 | 
			
		||||
    root_tasks_with_children = [
 | 
			
		||||
        root for root in root_tasks if dependency_graph.get(root, [])
 | 
			
		||||
    ]
 | 
			
		||||
    for root in sorted(
 | 
			
		||||
        root_tasks_with_children,
 | 
			
		||||
        key=lambda x: len(dependency_graph.get(x, [])),
 | 
			
		||||
        reverse=True,
 | 
			
		||||
    ):
 | 
			
		||||
        if root not in global_visited:
 | 
			
		||||
            dfs(root)
 | 
			
		||||
            dag_lines.append("")
 | 
			
		||||
 | 
			
		||||
    return "\n".join(dag_lines).rstrip()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def main(args):
 | 
			
		||||
    data = get_task_data()
 | 
			
		||||
    task_details, dependency_graph, root_tasks = parse_task_data(data)
 | 
			
		||||
    ascii_dag = render_dependency_dag(
 | 
			
		||||
        task_details, dependency_graph, root_tasks, show_id=args.show_id
 | 
			
		||||
    )
 | 
			
		||||
    print(ascii_dag)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
    parser = argparse.ArgumentParser(
 | 
			
		||||
        description="Generates a task dependency DAG for Taskwarrior tasks."
 | 
			
		||||
    )
 | 
			
		||||
    parser.add_argument(
 | 
			
		||||
        "--show-id",
 | 
			
		||||
        action="store_true",
 | 
			
		||||
        default=False,
 | 
			
		||||
        help="Include task IDs in the output.",
 | 
			
		||||
    )
 | 
			
		||||
    args = parser.parse_args()
 | 
			
		||||
    main(args)
 | 
			
		||||
		Loading…
	
		Reference in New Issue