support checkboxes

This commit is contained in:
James Turk 2024-07-27 01:54:20 -04:00
parent b0f81d7f86
commit d0d0816d79
2 changed files with 62 additions and 20 deletions

View File

@ -10,9 +10,9 @@ A suite of tools for dealing with a directory of markdown files.
- TODO: add config file - TODO: add config file
- [ ] make file types configurable - [ ] make file types configurable
- [ ] look in local dir, then XDG_HOME - [ ] look in local dir, then XDG_HOME
- TODO: support checkboxes - DONE: support checkboxes
- [ ] scan lists - [x] scan lists
- [ ] percent complete - [x] percent complete
- TODO: decide on more permanent name - TODO: decide on more permanent name
- IDEA: recurring? - IDEA: recurring?
- IDEA: track changes - IDEA: track changes

View File

@ -2,6 +2,7 @@ import click
import pathlib import pathlib
import datetime import datetime
import re import re
from dataclasses import dataclass
from dateutil import parser from dateutil import parser
from rich.table import Table from rich.table import Table
from rich.console import Console from rich.console import Console
@ -42,6 +43,51 @@ def scan_contents(file: pathlib.Path) -> dict:
return {"words": len(words), "todos": len(TODO_TODO_RE.findall(text))} return {"words": len(words), "todos": len(TODO_TODO_RE.findall(text))}
def render_checkbox(done: bool):
return "" if done else ""
@dataclass
class TodoItem:
file: str
status: str
description: str
tags: list[str]
style: str
subtasks: list[tuple[bool, str]]
def fields(self):
return [
"file",
"status",
"description",
"tags",
]
def to_row(self):
return [
self.file,
self.status + self.subtask_status(),
self.description + self.subtask_nested(),
" | ".join(self.tags),
]
def subtask_nested(self):
if not self.subtasks:
return ""
else:
return "\n" + "\n".join(
f"- {render_checkbox(s[0])} {s[1]}" for s in self.subtasks
)
def subtask_status(self):
total = len(self.subtasks)
if not total:
return ""
done = sum(1 if st[0] else 0 for st in self.subtasks)
return f" {done}/{total}"
def pull_todos(file: pathlib.Path): def pull_todos(file: pathlib.Path):
text = file.read_text().splitlines() text = file.read_text().splitlines()
active_todo = None active_todo = None
@ -61,22 +107,20 @@ def pull_todos(file: pathlib.Path):
style = "#999999" style = "#999999"
elif status == "IDEA": elif status == "IDEA":
style = "blue" style = "blue"
active_todo = { active_todo = TodoItem(
"file": file.name, file=file.name,
"status": status, status=status,
"description": description, description=description,
"tags": " | ".join(tag_strs), tags=tag_strs,
"style": style, style=style,
"subtasks": [], subtasks=[],
} )
elif active_todo: elif active_todo:
# check for checkbox if we're nested inside a todo # check for checkbox if we're nested inside a todo
checkbox = CHECKBOX_RE.match(line) checkbox = CHECKBOX_RE.match(line)
if checkbox: if checkbox:
checkbox_status, desc = checkbox.groups() checkbox_status, desc = checkbox.groups()
active_todo["subtasks"].append( active_todo.subtasks.append((checkbox_status == "x", desc))
{"status": checkbox_status, "description": desc}
)
else: else:
yield active_todo yield active_todo
active_todo = None active_todo = None
@ -96,19 +140,17 @@ def human_readable_date(dt: datetime.datetime) -> str:
return f"{int(delta.total_seconds() / 3600 / 24)}d ago" return f"{int(delta.total_seconds() / 3600 / 24)}d ago"
def lod_table(data: list[dict]) -> Table | str: def lod_table(data: list[TodoItem]) -> Table | str:
"""list of dicts to Table""" """list of dicts to Table"""
if not data: if not data:
return "no results" return "no results"
table = Table() table = Table()
for key in data[0].keys(): for key in data[0].fields():
if key != "style": table.add_column(key)
table.add_column(key)
for row in data: for row in data:
style = row.pop("style", None) table.add_row(*row.to_row(), style=row.style)
table.add_row(*(str(x) for x in row.values()), style=style)
return table return table