02-todo-cli #4

Open
uda wants to merge 3 commits from 02-todo-cli into 01-todo-list
4 changed files with 188 additions and 0 deletions

3
README.md Normal file
View file

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

8
breakitlive.sh Normal file
View file

@ -0,0 +1,8 @@
#doitlive speed: 3
#doitlive prompt: sorin
python3 todo.py add Test
python3 todo.py list Test
python3 todo.py list Test Test
python3 todo.py add test1 Test Test

29
doitlive.sh Normal file
View file

@ -0,0 +1,29 @@
#doitlive speed: 3
#doitlive prompt: sorin
python3 todo.py --help
python3 todo.py help
python3 todo.py -h
python3 todo.py h; echo $?
python3 todo.py asdf; echo $?
python3 todo.py list
python3 todo.py add Test
python3 todo.py list
python3 todo.py list Test
python3 todo.py add test Test
python3 todo.py list Test
python3 todo.py list
python3 todo.py complete test Test
python3 todo.py list Test
python3 todo.py list
python3 todo.py complete Test
python3 todo.py list
python3 todo.py remove test Test
python3 todo.py list Test
python3 todo.py list
python3 todo.py remove Test
python3 todo.py list

148
todo.py Executable file
View file

@ -0,0 +1,148 @@
#!/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(render_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:
if parent not in todo:
print(f"Could not find parent task '{parent}' in root task list")
exit(1)
if task not in todo[parent]["sub_tasks"]:
print(f"Could not find task '{task}' in parent task '{parent}'")
exit(1)
if task not in todo:
print(f"Could not find task '{task}' 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:
if parent not in todo:
print(f"Could not find parent task '{parent}' in root task list")
exit(1)
if task not in todo[parent]["sub_tasks"]:
print(f"Could not find task '{task}' in parent task '{parent}'")
exit(1)
if task not in todo:
print(f"Could not find task '{task}' 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 render_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(render_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()