Create a python todo utility

This commit is contained in:
Yehuda Deutsch 2024-11-11 16:06:27 -05:00
parent 4510216590
commit 4cf1060af8
Signed by: uda
GPG key ID: 8EF44B89374262A5
2 changed files with 137 additions and 0 deletions

3
README.md Normal file
View file

@ -0,0 +1,3 @@
# Ajal Todo CLI
A todo utility

134
todo.py Executable file
View file

@ -0,0 +1,134 @@
#!/usr/bin/env python3
import os
import re
import sys
base_dir = os.path.abspath(os.path.dirname(__file__))
todo_file = os.path.join(base_dir, "todo.md")
def add_task(task: str, parent=None):
print(f"Adding task '{task}' to '{parent if parent else 'root'}'")
todo = parse_todo()
if parent and parent not in todo:
print(f"Could not find parent task '{parent}' in root task list")
exit(1)
if parent:
todo[parent]["sub_tasks"][task] = {"completed": False}
else:
todo[task] = {"completed": False, "sub_tasks": {}}
write_todo(todo)
def list_tasks(parent=None):
print(f"Listing tasks in '{parent if parent else 'root'}'")
todo = parse_todo()
if parent and parent not in todo:
print(f"Could not find parent task '{parent}' in root task list")
exit(1)
if parent:
todo = {parent: todo[parent]}
print(build_todo(todo))
def complete_task(task, parent=None):
print(f"Marking task '{task}' in '{parent if parent else 'root'}' as completed")
todo = parse_todo()
if parent and parent not in todo:
print(f"Could not find parent task '{parent}' in root task list")
exit(1)
if parent:
task = todo[parent]["sub_tasks"][task]
else:
task = todo[task]
task["completed"] = True
write_todo(todo)
def remove_task(task, parent=None):
print(f"Removing task '{task}' from '{parent if parent else 'root'}'")
todo = parse_todo()
if parent and parent not in todo:
print(f"Could not find parent task '{parent}' in root task list")
exit(1)
if parent:
del todo[parent]["sub_tasks"][task]
else:
del todo[task]
write_todo(todo)
def print_help():
print("""Usage: todo.py [add|complete|remove] TASK [PARENT]
todo.py list PARENT
Manages todo.md file as a ToDo file""")
def parse_todo() -> dict[str, dict]:
todo = {}
with open(todo_file, "r") as f:
lines = f.readlines()
root_task = None
for line in lines:
if matches := re.match(r"^-\s+(\[(?P<status>[x ])]\s+)?(?P<task>.*)$", line.rstrip()):
root_task_name = matches.group("task")
root_task = {
"completed": matches.group("status") == "x",
"sub_tasks": {},
}
todo[root_task_name] = root_task
elif matches := re.match(r"^\s+-\s+(\[(?P<status>[x ])]\s+)?(?P<task>.*)$", line.rstrip()):
leaf_task = matches.group("task")
root_task["sub_tasks"][leaf_task] = {
"completed": matches.group("status") == "x",
}
return todo
def build_todo(todo: dict[str, dict]) -> str:
lines = ["# A ToDo list", ""]
for task, task_data in todo.items(): # type: str, dict
lines.append(f"- [{'x' if task_data["completed"] else ' '}] {task}")
for leaf_task, leaf_task_data in task_data["sub_tasks"].items():
lines.append(f" - [{'x' if leaf_task_data["completed"] else ' '}] {leaf_task}")
lines += [""]
return "\n".join(lines)
def write_todo(todo: dict[str, dict]):
with open(todo_file, "w") as f:
f.write(build_todo(todo))
def main():
if len(sys.argv) < 2:
print_help()
exit(1)
match sys.argv[1]:
case "help" | "--help" | "h" | "-h":
print_help()
case "list":
match sys.argv[2:]:
case [parent]:
list_tasks(parent)
case _:
list_tasks()
case "add" | "complete" | "remove" as task_name:
task_func = f"{task_name}_task"
match sys.argv[2:]:
case [task]:
globals()[task_func](task)
case [task, parent]:
globals()[task_func](task, parent)
case _:
print_help()
exit(1)
case _:
print_help()
exit(1)
if __name__ == "__main__":
main()