From 0769d656102d7948160f5a4f86765a36daad34f9 Mon Sep 17 00:00:00 2001 From: Marty Oehme Date: Mon, 31 Mar 2025 21:01:35 +0200 Subject: [PATCH 1/6] Only use standard pyright checking mode --- pyproject.toml | 3 +++ 1 file changed, 3 insertions(+) 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" From dca5ebb9fb9a5d8167191bf1e54c4f05724312d6 Mon Sep 17 00:00:00 2001 From: Marty Oehme Date: Mon, 31 Mar 2025 21:01:35 +0200 Subject: [PATCH 2/6] Extract code into individual functions --- topen | 97 +++++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 74 insertions(+), 23 deletions(-) diff --git a/topen b/topen index 2103006..c39a073 100755 --- a/topen +++ b/topen @@ -5,6 +5,7 @@ # Edits an existing task note file, # or creates a new one. +import argparse import subprocess import sys from pathlib import Path @@ -13,38 +14,88 @@ from tasklib import Task, TaskWarrior TASK_DATA_DIR = "~/.local/share/task" -TASKNOTES_DIR = "~/.local/share/task/notes" -TASKNOTES_EXT = "md" -TASKNOTES_ANNOT = "Note" +TOPEN_DIR = "~/.local/share/task/notes" +TOPEN_EXT = "md" +TOPEN_ANNOT = "Note" -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", + ) + _ = parser.add_argument( + "-x", "--extension", default=TOPEN_EXT, help="Extension of note files" + ) + _ = parser.add_argument( + "-t", + "--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" + ) -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) +def main(): + args = parse_cli() -_ = sys.stderr.write(f"Editing note for: {t['description']} ({t['uuid']})\n") + if not args.id: + _ = sys.stderr.write("Please provide task ID as argument.\n") -notes_file = Path(TASKNOTES_DIR).joinpath(f"{t['uuid']}.{TASKNOTES_EXT}") + 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) -proc = subprocess.Popen(f"nvim {notes_file}", shell=True) -_ = proc.wait() + # TODO: Add editor choice + open_editor(fname) -def add_annotation_if_missing(task: Task) -> None: - for annot in t["annotations"]: - if annot["description"] == "Note": + 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 = "nvim") -> None: + _ = sys.stderr.write(f"Editing note {file}\n") + 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) + _ = sys.stderr.write("Added annotation.\n") -add_annotation_if_missing(t) +if __name__ == "__main__": + main() From 9e9b840ed08b5f2b8399ad5657b356c501f718f1 Mon Sep 17 00:00:00 2001 From: Marty Oehme Date: Mon, 31 Mar 2025 21:32:00 +0200 Subject: [PATCH 3/6] Add environment variables --- topen | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/topen b/topen index c39a073..f3bbd54 100755 --- a/topen +++ b/topen @@ -5,18 +5,24 @@ # 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") -TOPEN_DIR = "~/.local/share/task/notes" -TOPEN_EXT = "md" -TOPEN_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") def parse_cli() -> argparse.Namespace: From b1f63590c57067b5aa039e3667f169adc3f9c9de Mon Sep 17 00:00:00 2001 From: Marty Oehme Date: Mon, 31 Mar 2025 21:32:35 +0200 Subject: [PATCH 4/6] Let user set editor or get from EDITOR --- topen | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/topen b/topen index f3bbd54..a294440 100755 --- a/topen +++ b/topen @@ -24,6 +24,8 @@ 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") + def parse_cli() -> argparse.Namespace: parser = argparse.ArgumentParser(description="Taskwarrior note editing made easy.") @@ -48,6 +50,9 @@ def parse_cli() -> argparse.Namespace: _ = 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" + ) return parser.parse_args() @@ -65,8 +70,7 @@ def main(): sys.exit(1) fname = get_notes_file(uuid, notes_dir=args.notes_dir, notes_ext=args.extension) - # TODO: Add editor choice - open_editor(fname) + open_editor(fname, editor=args.editor) add_annotation_if_missing(task, annotation_content=args.annotation) @@ -87,7 +91,7 @@ def get_notes_file( return Path(notes_dir).joinpath(f"{uuid}.{notes_ext}") -def open_editor(file: Path, editor: str = "nvim") -> None: +def open_editor(file: Path, editor: str = TOPEN_EDITOR) -> None: _ = sys.stderr.write(f"Editing note {file}\n") proc = subprocess.Popen(f"{editor} {file}", shell=True) _ = proc.wait() From 3481e81c93a16e633487a215cabeb671c69428e5 Mon Sep 17 00:00:00 2001 From: Marty Oehme Date: Mon, 31 Mar 2025 21:32:35 +0200 Subject: [PATCH 5/6] Remove short style for rare options --- topen | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/topen b/topen index a294440..dbe7411 100755 --- a/topen +++ b/topen @@ -36,13 +36,12 @@ def parse_cli() -> argparse.Namespace: "-d", "--notes-dir", default=TOPEN_DIR, - help="Location of topen notes", + help="Location of topen notes files", ) _ = parser.add_argument( - "-x", "--extension", default=TOPEN_EXT, help="Extension of note files" + "--extension", default=TOPEN_EXT, help="Extension of note files" ) _ = parser.add_argument( - "-t", "--annotation", default=TOPEN_ANNOT, help="Annotation content to set within taskwarrior", From 678d986e8fbba89b5554b7ca63bf28873e836739 Mon Sep 17 00:00:00 2001 From: Marty Oehme Date: Mon, 31 Mar 2025 21:42:11 +0200 Subject: [PATCH 6/6] Add quiet mode --- topen | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/topen b/topen index dbe7411..9cb6091 100755 --- a/topen +++ b/topen @@ -23,8 +23,8 @@ TASK_DATA_DIR = os.getenv("TASKDATA", "~/.local/share/task") 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) def parse_cli() -> argparse.Namespace: @@ -38,6 +38,12 @@ def parse_cli() -> argparse.Namespace: 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" ) @@ -56,16 +62,27 @@ def parse_cli() -> argparse.Namespace: return parser.parse_args() +IS_QUIET = False + + +def whisper(text: str) -> None: + if not IS_QUIET: + print(text) + + 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.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) @@ -91,7 +108,7 @@ def get_notes_file( def open_editor(file: Path, editor: str = TOPEN_EDITOR) -> None: - _ = sys.stderr.write(f"Editing note {file}\n") + _ = whisper(f"Editing note: {file}") proc = subprocess.Popen(f"{editor} {file}", shell=True) _ = proc.wait() @@ -103,7 +120,7 @@ def add_annotation_if_missing( if annot["description"] == annotation_content: return task.add_annotation(annotation_content) - _ = sys.stderr.write("Added annotation.\n") + _ = whisper(f"Added annotation: {annotation_content}") if __name__ == "__main__":