diff --git a/pyproject.toml b/pyproject.toml index 7f9d7ab..0bb91b0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,3 +7,6 @@ requires-python = ">=3.13" dependencies = [ "tasklib>=2.5.1", ] + +[tool.pyright] +typeCheckingMode = "standard" diff --git a/topen b/topen index 2103006..9cb6091 100755 --- a/topen +++ b/topen @@ -5,46 +5,123 @@ # Edits an existing task note file, # or creates a new one. +# It currently assumes an XDG-compliant taskwarrior configuration by default. + +import argparse +import os import subprocess import sys from pathlib import Path from tasklib import Task, TaskWarrior -TASK_DATA_DIR = "~/.local/share/task" +# TODO: This should not assume XDG compliance for +# no-setup TW instances. +TASK_RC = os.getenv("TASKRC", "~/.config/task/taskrc") +TASK_DATA_DIR = os.getenv("TASKDATA", "~/.local/share/task") -TASKNOTES_DIR = "~/.local/share/task/notes" -TASKNOTES_EXT = "md" -TASKNOTES_ANNOT = "Note" +TOPEN_DIR = os.getenv("TOPEN_DIR", "~/.local/share/task/notes") +TOPEN_EXT = os.getenv("TOPEN_EXT", "md") +TOPEN_ANNOT = os.getenv("TOPEN_ANNOT", "Note") +TOPEN_EDITOR = os.getenv("EDITOR") or os.getenv("VISUAL", "nano") +TOPEN_QUIET = os.getenv("TOPEN_QUIET", False) -tw = TaskWarrior(data_location=TASK_DATA_DIR) -args = sys.argv +def parse_cli() -> argparse.Namespace: + parser = argparse.ArgumentParser(description="Taskwarrior note editing made easy.") + _ = parser.add_argument( + "id", help="The id/uuid of the taskwarrior task for which we edit notes" + ) + _ = parser.add_argument( + "-d", + "--notes-dir", + default=TOPEN_DIR, + help="Location of topen notes files", + ) + _ = parser.add_argument( + "--quiet", + default=TOPEN_QUIET, + action="store_true", + help="Silence any verbose displayed information", + ) + _ = parser.add_argument( + "--extension", default=TOPEN_EXT, help="Extension of note files" + ) + _ = parser.add_argument( + "--annotation", + default=TOPEN_ANNOT, + help="Annotation content to set within taskwarrior", + ) + _ = parser.add_argument( + "--task-data", default=TASK_DATA_DIR, help="Location of taskwarrior data" + ) + _ = parser.add_argument( + "--editor", default=TOPEN_EDITOR, help="Program to open note files with" + ) -if not len(args) == 2: - _ = sys.stderr.write("Please provide task ID as argument.\n") - sys.exit(1) + return parser.parse_args() -given_id = args[1] -try: - t = tw.tasks.get(id=given_id) -except Task.DoesNotExist: - t = tw.tasks.get(uuid=given_id) +IS_QUIET = False -_ = sys.stderr.write(f"Editing note for: {t['description']} ({t['uuid']})\n") -notes_file = Path(TASKNOTES_DIR).joinpath(f"{t['uuid']}.{TASKNOTES_EXT}") +def whisper(text: str) -> None: + if not IS_QUIET: + print(text) -proc = subprocess.Popen(f"nvim {notes_file}", shell=True) -_ = proc.wait() -def add_annotation_if_missing(task: Task) -> None: - for annot in t["annotations"]: - if annot["description"] == "Note": +def main(): + args = parse_cli() + + if not args.id: + _ = sys.stderr.write("Please provide task ID as argument.\n") + if args.quiet: + global IS_QUIET + IS_QUIET = True + + task = get_task(id=args.id, data_location=args.task_data) + uuid = task["uuid"] + if not uuid: + _ = sys.stderr.write(f"Could not find task for ID: {args.id}.") + sys.exit(1) + fname = get_notes_file(uuid, notes_dir=args.notes_dir, notes_ext=args.extension) + + open_editor(fname, editor=args.editor) + + add_annotation_if_missing(task, annotation_content=args.annotation) + + +def get_task(id: str, data_location: str = TASK_DATA_DIR) -> Task: + tw = TaskWarrior(data_location) + try: + t = tw.tasks.get(id=id) + except Task.DoesNotExist: + t = tw.tasks.get(uuid=id) + + return t + + +def get_notes_file( + uuid: str, notes_dir: str = TOPEN_DIR, notes_ext: str = TOPEN_EXT +) -> Path: + return Path(notes_dir).joinpath(f"{uuid}.{notes_ext}") + + +def open_editor(file: Path, editor: str = TOPEN_EDITOR) -> None: + _ = whisper(f"Editing note: {file}") + proc = subprocess.Popen(f"{editor} {file}", shell=True) + _ = proc.wait() + + +def add_annotation_if_missing( + task: Task, annotation_content: str = TOPEN_ANNOT +) -> None: + for annot in task["annotations"] or []: + if annot["description"] == annotation_content: return - t.add_annotation(TASKNOTES_ANNOT) - _ = sys.stderr.write(f"Added annotation.\n") + task.add_annotation(annotation_content) + _ = whisper(f"Added annotation: {annotation_content}") -add_annotation_if_missing(t) +if __name__ == "__main__": + main()