Compare commits
6 commits
a37d0e9272
...
678d986e8f
| Author | SHA1 | Date | |
|---|---|---|---|
| 678d986e8f | |||
| 3481e81c93 | |||
| b1f63590c5 | |||
| 9e9b840ed0 | |||
| dca5ebb9fb | |||
| 0769d65610 |
2 changed files with 104 additions and 24 deletions
|
|
@ -7,3 +7,6 @@ requires-python = ">=3.13"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"tasklib>=2.5.1",
|
"tasklib>=2.5.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[tool.pyright]
|
||||||
|
typeCheckingMode = "standard"
|
||||||
|
|
|
||||||
125
topen
125
topen
|
|
@ -5,46 +5,123 @@
|
||||||
# Edits an existing task note file,
|
# Edits an existing task note file,
|
||||||
# or creates a new one.
|
# or creates a new one.
|
||||||
|
|
||||||
|
# It currently assumes an XDG-compliant taskwarrior configuration by default.
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from tasklib import Task, TaskWarrior
|
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"
|
TOPEN_DIR = os.getenv("TOPEN_DIR", "~/.local/share/task/notes")
|
||||||
TASKNOTES_EXT = "md"
|
TOPEN_EXT = os.getenv("TOPEN_EXT", "md")
|
||||||
TASKNOTES_ANNOT = "Note"
|
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:
|
return parser.parse_args()
|
||||||
_ = sys.stderr.write("Please provide task ID as argument.\n")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
given_id = args[1]
|
|
||||||
|
|
||||||
try:
|
IS_QUIET = False
|
||||||
t = tw.tasks.get(id=given_id)
|
|
||||||
except Task.DoesNotExist:
|
|
||||||
t = tw.tasks.get(uuid=given_id)
|
|
||||||
|
|
||||||
_ = 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:
|
def main():
|
||||||
for annot in t["annotations"]:
|
args = parse_cli()
|
||||||
if annot["description"] == "Note":
|
|
||||||
|
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
|
return
|
||||||
t.add_annotation(TASKNOTES_ANNOT)
|
task.add_annotation(annotation_content)
|
||||||
_ = sys.stderr.write(f"Added annotation.\n")
|
_ = whisper(f"Added annotation: {annotation_content}")
|
||||||
|
|
||||||
|
|
||||||
add_annotation_if_missing(t)
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue