Skip to content

Commit

Permalink
Add todo tasks
Browse files Browse the repository at this point in the history
  • Loading branch information
goFrendiAsgard committed Nov 27, 2024
1 parent 73b0bfd commit 259be4a
Show file tree
Hide file tree
Showing 3 changed files with 141 additions and 10 deletions.
4 changes: 3 additions & 1 deletion src/zrb/builtin/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from zrb.builtin.shell.autocomplete.bash import make_bash_autocomplete
from zrb.builtin.shell.autocomplete.subcmd import get_shell_subcommands
from zrb.builtin.shell.autocomplete.zsh import make_zsh_autocomplete
from zrb.builtin.todo import todo_add, todo_edit, todo_list
from zrb.builtin.todo import todo_add, todo_complete, todo_edit, todo_list, todo_log

assert create_project
assert add_fastapp_to_project
Expand All @@ -39,3 +39,5 @@
assert todo_list
assert todo_add
assert todo_edit
assert todo_complete
assert todo_log
92 changes: 83 additions & 9 deletions src/zrb/builtin/todo.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@
from zrb.task.make_task import make_task
from zrb.util.todo import (
TodoTaskModel,
add_durations,
cascade_todo_task,
get_visual_todo_list,
line_to_todo_task,
load_todo_list,
save_todo_list,
select_todo_task,
todo_task_to_line,
)

Expand Down Expand Up @@ -49,12 +51,12 @@
)
def todo_add(ctx: AnyContext):
todo_file_path = os.path.join(TODO_DIR, "todo.txt")
todo_tasks: list[TodoTaskModel] = []
todo_list: list[TodoTaskModel] = []
if os.path.isfile(todo_file_path):
todo_tasks = load_todo_list(todo_file_path)
todo_list = load_todo_list(todo_file_path)
else:
os.makedirs(TODO_DIR, exist_ok=True)
todo_tasks.append(
todo_list.append(
cascade_todo_task(
TodoTaskModel(
priority=ctx.input.priority.upper(),
Expand All @@ -72,8 +74,8 @@ def todo_add(ctx: AnyContext):
)
)
)
save_todo_list(todo_file_path, todo_tasks)
return get_visual_todo_list(todo_tasks)
save_todo_list(todo_file_path, todo_list)
return get_visual_todo_list(todo_list)


@make_task(name="todo-list", description="📋 List todo", group=todo_group, alias="list")
Expand All @@ -85,6 +87,78 @@ def todo_list(ctx: AnyContext):
return get_visual_todo_list(todo_tasks)


@make_task(
name="todo-complete",
input=StrInput(name="keyword", prompt="Task keyword", description="Task Keyword"),
description="✅ Complete todo",
group=todo_group,
alias="complete",
)
def todo_complete(ctx: AnyContext):
todo_file_path = os.path.join(TODO_DIR, "todo.txt")
todo_list: list[TodoTaskModel] = []
if os.path.isfile(todo_file_path):
todo_list = load_todo_list(todo_file_path)
# Get todo task
todo_task = select_todo_task(todo_list, ctx.input.keyword)
if todo_task is None:
raise Exception("Task not found")
# Update todo task
if todo_task.creation_date is not None:
todo_task.completion_date = datetime.date.today()
todo_task.completed = True
# Save todo list
save_todo_list(todo_list)
return get_visual_todo_list(todo_list)


@make_task(
name="todo-log",
input=[
StrInput(name="keyword", prompt="Task keyword", description="Task Keyword"),
StrInput(
name="start",
prompt="Working start time (%Y-%m-%d %H:%M:%S)",
description="Working start time",
default_str=lambda _: _get_default_start(),
),
StrInput(
name="duration",
prompt="Working duration",
description="Working duration",
default_str="30m",
),
StrInput(
name="log",
prompt="Working log",
description="Working log",
),
],
description="🕒 Log work todo",
group=todo_group,
alias="log",
)
def todo_log(ctx: AnyContext):
todo_file_path = os.path.join(TODO_DIR, "todo.txt")
todo_list: list[TodoTaskModel] = []
if os.path.isfile(todo_file_path):
todo_list = load_todo_list(todo_file_path)
# Get todo task
todo_task = select_todo_task(todo_list, ctx.input.keyword)
if todo_task is None:
raise Exception("Task not found")
# Update todo task
current_duration = todo_task.keyval.get("duration", "0")
todo_task.keyval["duration"] = add_durations(current_duration, ctx.input.duration)
# Save todo list
save_todo_list(todo_list)
return get_visual_todo_list(todo_list)


def _get_default_start() -> str:
return datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")


@make_task(
name="todo-edit",
input=[
Expand All @@ -100,17 +174,17 @@ def todo_list(ctx: AnyContext):
alias="edit",
)
def todo_edit(ctx: AnyContext):
todo_tasks = [
todo_list = [
cascade_todo_task(line_to_todo_task(line))
for line in ctx.input.text.split("\n")
if line.strip() != ""
]
new_content = "\n".join(todo_task_to_line(todo_task) for todo_task in todo_tasks)
new_content = "\n".join(todo_task_to_line(todo_task) for todo_task in todo_list)
todo_file_path = os.path.join(TODO_DIR, "todo.txt")
with open(todo_file_path, "w") as f:
f.write(new_content)
todo_tasks = load_todo_list(todo_file_path)
return get_visual_todo_list(todo_tasks)
todo_list = load_todo_list(todo_file_path)
return get_visual_todo_list(todo_list)


def _get_todo_txt_content() -> str:
Expand Down
55 changes: 55 additions & 0 deletions src/zrb/util/todo.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,28 @@ def cascade_todo_task(todo_task: TodoTaskModel):
return todo_task


def select_todo_task(
todo_list: list[TodoTaskModel], keyword: str
) -> TodoTaskModel | None:
for todo_task in todo_list:
id = todo_task.keyval.get("id", "")
if keyword.lower().strip() == id.lower.strip():
return todo_task
for todo_task in todo_list:
description = todo_task.description
if keyword.lower().strip() == description.lower.strip():
return todo_task
for todo_task in todo_list:
id = todo_task.keyval.get("id", "")
if keyword.lower().strip() in id.lower.strip():
return todo_task
for todo_task in todo_list:
description = todo_task.description
if keyword.lower().strip() in description.lower.strip():
return todo_task
return None


def load_todo_list(todo_file_path: str) -> list[TodoTaskModel]:
with open(todo_file_path, "r") as f:
todo_lines = f.read().strip().split("\n")
Expand Down Expand Up @@ -202,3 +224,36 @@ def _date_to_str(date: datetime.date | None) -> str:
if date is None:
return "".ljust(14)
return date.strftime("%a %Y-%m-%d")


def add_durations(duration1: str, duration2: str) -> str:
total_seconds = _parse_duration(duration1) + _parse_duration(duration2)
# Format and return the result
return _format_duration(total_seconds)


def _parse_duration(duration: str) -> int:
"""Parse a duration string into total seconds."""
units = {"M": 2592000, "w": 604800, "d": 86400, "h": 3600, "m": 60, "s": 1}
total_seconds = 0
match = re.findall(r"(\d+)([Mwdhms])", duration)
for value, unit in match:
total_seconds += int(value) * units[unit]
return total_seconds


def _format_duration(total_seconds: int) -> str:
"""Format total seconds into a duration string."""
units = [
("w", 604800), # 7 days in a week
("d", 86400), # 24 hours in a day
("h", 3600), # 60 minutes in an hour
("m", 60), # 60 seconds in a minute
("s", 1), # seconds
]
result = []
for unit, value_in_seconds in units:
if total_seconds >= value_in_seconds:
amount, total_seconds = divmod(total_seconds, value_in_seconds)
result.append(f"{amount}{unit}")
return " ".join(result) if result else "0s"

0 comments on commit 259be4a

Please sign in to comment.