aboutsummaryrefslogtreecommitdiffstats
path: root/modules/by-name/ti
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--modules/by-name/ti/timewarrior/module.nix45
-rw-r--r--modules/by-name/ti/timewarrior/nord.theme.license9
-rwxr-xr-xmodules/by-name/ti/timewarrior/taskwarirror_hooks/on-modify_track-timewarrior.py94
-rwxr-xr-xmodules/by-name/ti/timewarrior/taskwarrior_hooks/on-modify.track-timewarrior.py109
-rwxr-xr-xmodules/by-name/ti/timewarrior/taskwarrior_hooks/on-modify.track-total-active-time.py (renamed from modules/by-name/ti/timewarrior/taskwarirror_hooks/on-modify_track-total-active-time.py)52
5 files changed, 153 insertions, 156 deletions
diff --git a/modules/by-name/ti/timewarrior/module.nix b/modules/by-name/ti/timewarrior/module.nix
index d6b4dc32..90a541d8 100644
--- a/modules/by-name/ti/timewarrior/module.nix
+++ b/modules/by-name/ti/timewarrior/module.nix
@@ -1,3 +1,12 @@
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
{
config,
lib,
@@ -7,49 +16,41 @@
cfg = config.soispha.programs.timewarrior;
track_timewarrior = pkgs.stdenv.mkDerivation {
- name = "track_timewarrior.taskwarrior-hook";
+ name = "track-timewarrior";
nativeBuildInputs = [
pkgs.makeWrapper
];
buildInputs = [
pkgs.timewarrior
- pkgs.taskwarrior
- # TODO: Use a `taskw` package, that actually supports newer python variants <2024-07-13>
- (pkgs.python311.withPackages (pythonPackages:
- with pythonPackages; [
- taskw
- ]))
+ pkgs.taskwarrior3
+ pkgs.python3
];
dontUnpack = true;
installPhase = ''
- install -Dm755 ${./taskwarirror_hooks/on-modify_track-timewarrior.py} $out/bin/bin
- wrapProgram $out/bin/bin \
- --prefix PATH : ${lib.makeBinPath [pkgs.taskwarrior pkgs.timewarrior]}
+ install -Dm755 ${./taskwarrior_hooks/on-modify.track-timewarrior.py} $out/bin/track-timewarrior
+ wrapProgram $out/bin/track-timewarrior \
+ --set PATH ${lib.makeBinPath [pkgs.taskwarrior3 pkgs.timewarrior]}
'';
- meta.mainProgram = "bin";
+ meta.mainProgram = "track-timewarrior";
};
track_total_active_time = pkgs.stdenv.mkDerivation {
- name = "track_total_active_time.taskwarrior-hook";
+ name = "track-total-active-time";
nativeBuildInputs = [
pkgs.makeWrapper
];
buildInputs = [
- pkgs.taskwarrior
- # TODO: Use a `taskw` package, that actually supports newer python variants <2024-07-13>
- (pkgs.python311.withPackages (pythonPackages:
- with pythonPackages; [
- taskw
- ]))
+ pkgs.taskwarrior3
+ pkgs.python3
];
dontUnpack = true;
installPhase = ''
- install -Dm755 ${./taskwarirror_hooks/on-modify_track-total-active-time.py} $out/bin/bin
- wrapProgram $out/bin/bin \
- --prefix PATH : ${lib.makeBinPath [pkgs.taskwarrior]}
+ install -Dm755 ${./taskwarrior_hooks/on-modify.track-total-active-time.py} $out/bin/track-total-active-time
+ wrapProgram $out/bin/track-total-active-time \
+ --set PATH ${lib.makeBinPath [pkgs.taskwarrior3]}
'';
- meta.mainProgram = "bin";
+ meta.mainProgram = "track-total-active-time";
};
in {
options.soispha.programs.timewarrior = {
diff --git a/modules/by-name/ti/timewarrior/nord.theme.license b/modules/by-name/ti/timewarrior/nord.theme.license
new file mode 100644
index 00000000..eae6a84c
--- /dev/null
+++ b/modules/by-name/ti/timewarrior/nord.theme.license
@@ -0,0 +1,9 @@
+nixos-config - My current NixOS configuration
+
+Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+SPDX-License-Identifier: GPL-3.0-or-later
+
+This file is part of my nixos-config.
+
+You should have received a copy of the License along with this program.
+If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
diff --git a/modules/by-name/ti/timewarrior/taskwarirror_hooks/on-modify_track-timewarrior.py b/modules/by-name/ti/timewarrior/taskwarirror_hooks/on-modify_track-timewarrior.py
deleted file mode 100755
index b482af6a..00000000
--- a/modules/by-name/ti/timewarrior/taskwarirror_hooks/on-modify_track-timewarrior.py
+++ /dev/null
@@ -1,94 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-# Copyright (C) 2016-present Arctic Ice Studio <development@arcticicestudio.com>
-# Copyright (C) 2016-present Sven Greb <development@svengreb.de>
-
-# Project: igloo
-# Repository: https://github.com/arcticicestudio/igloo
-# License: MIT
-# References:
-# https://taskwarrior.org/docs
-# https://taskwarrior.org/docs/timewarrior
-# timew(1)
-# task(1)
-
-"""A Taskwarrior hook to track the time of a active task with Taskwarrior.
-
-This hook will extract all of the following for use as Timewarrior tags:
-
-* UUID
-* Project
-* Tags
-* Description
-* UDAs
-
-Note:
- This hook requires Python 3 and is only compatible with Taskwarrior version greater or equal to 2.4!
-
-This hook is a fork from the `official on-modify.timewarrior hook`_.
-
-.. _`official on-modify.timewarrior hook`:
- https://github.com/GothenburgBitFactory/timewarrior/blob/dev/ext/on-modify.timewarrior
-"""
-
-import subprocess
-import sys
-from json import loads, dumps
-from os import system
-from sys import stdin
-from taskw import TaskWarrior
-
-# Make no changes to the task, simply observe.
-old = loads(stdin.readline())
-new = loads(stdin.readline())
-print(dumps(new))
-
-
-w = TaskWarrior(config_filename=sys.argv[4].replace("rc:", ""))
-config = w.load_config(config_filename=sys.argv[4].replace("rc:", ""))
-if "max_active_tasks" in config:
- MAX_ACTIVE = int(config["max_active_tasks"])
-else:
- MAX_ACTIVE = 1
-
-
-# Extract attributes for use as tags.
-tags = [new["description"]]
-
-if "project" in new:
- project = new["project"]
- tags.append(project)
- if "." in project:
- tags.extend([tag for tag in project.split(".")])
-
-if "tags" in new:
- tags.extend(new["tags"])
-
-combined = " ".join(["'%s'" % tag for tag in tags]).encode("utf-8").strip()
-
-# Task has been started.
-if "start" in new and "start" not in old:
- # Prevent this task from starting if "task +ACTIVE count" is greater than "MAX_ACTIVE".
- p = subprocess.Popen(
- ["task", "+ACTIVE", "status:pending", "count", "rc.verbose:off"],
- stdout=subprocess.PIPE,
- )
- out, err = p.communicate()
- count = int(out.rstrip())
- if count >= MAX_ACTIVE:
- print(
- "Only %d task(s) can be active at a time. "
- "See 'max_active_tasks' in .taskrc." % MAX_ACTIVE
- )
- sys.exit(1)
-
- system("timew start " + combined.decode() + " :yes")
-
-# Task has been stopped.
-elif "start" not in new and "start" in old:
- system("timew stop " + combined.decode() + " :yes")
-
-# Any task that is active, with a non-pending status should not be tracked.
-elif "start" in new and new["status"] != "pending":
- system("timew stop " + combined.decode() + " :yes")
diff --git a/modules/by-name/ti/timewarrior/taskwarrior_hooks/on-modify.track-timewarrior.py b/modules/by-name/ti/timewarrior/taskwarrior_hooks/on-modify.track-timewarrior.py
new file mode 100755
index 00000000..3b42b3f2
--- /dev/null
+++ b/modules/by-name/ti/timewarrior/taskwarrior_hooks/on-modify.track-timewarrior.py
@@ -0,0 +1,109 @@
+#!/usr/bin/env python3
+
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# Copyright 2016 - 2021, 2023, Gothenburg Bit Factory
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
+import json
+import subprocess
+import sys
+
+# Hook should extract all the following for use as Timewarrior tags:
+# UUID
+# Project
+# Tags
+# Description
+# UDAs
+
+try:
+ input_stream = sys.stdin.buffer
+except AttributeError:
+ input_stream = sys.stdin
+
+
+MAX_ACTIVE = 1
+
+
+def extract_tags_from(json_obj) -> [str]:
+ # Extract attributes for use as tags.
+ tags = [json_obj["description"]]
+
+ if "project" in json_obj:
+ tags.append(json_obj["project"])
+
+ if "tags" in json_obj:
+ if type(json_obj["tags"]) is str:
+ # Usage of tasklib (e.g. in taskpirate) converts the tag list into a string
+ # If this is the case, convert it back into a list first
+ # See https://github.com/tbabej/taskpirate/issues/11
+ tags.extend(json_obj["tags"].split(","))
+ else:
+ tags.extend(json_obj["tags"])
+
+ return tags
+
+
+def extract_annotation_from(json_obj):
+ if "annotations" not in json_obj:
+ return "''"
+
+ return json_obj["annotations"][0]["description"]
+
+
+def main(old, new):
+ start_or_stop = ""
+
+ # Started task.
+ if "start" in new and "start" not in old:
+ # Prevent this task from starting if "task +ACTIVE count" is greater than "MAX_ACTIVE".
+ p = subprocess.Popen(
+ ["task", "+ACTIVE", "status:pending", "count", "rc.verbose:off"],
+ stdout=subprocess.PIPE,
+ )
+ out, err = p.communicate()
+ count = int(out.rstrip())
+ if count >= MAX_ACTIVE:
+ print(
+ f"Only {MAX_ACTIVE} task(s) can be active at a time.",
+ )
+ sys.exit(1)
+ else:
+ start_or_stop = "start"
+
+ # Stopped task.
+ elif ("start" not in new or "end" in new) and "start" in old:
+ start_or_stop = "stop"
+
+ if start_or_stop:
+ tags = extract_tags_from(new)
+
+ subprocess.call(["timew", start_or_stop] + tags + [":yes"])
+
+ # Modifications to task other than start/stop
+ elif "start" in new and "start" in old:
+ old_tags = extract_tags_from(old)
+ new_tags = extract_tags_from(new)
+
+ if old_tags != new_tags:
+ subprocess.call(["timew", "untag", "@1"] + old_tags + [":yes"])
+ subprocess.call(["timew", "tag", "@1"] + new_tags + [":yes"])
+
+ old_annotation = extract_annotation_from(old)
+ new_annotation = extract_annotation_from(new)
+
+ if old_annotation != new_annotation:
+ subprocess.call(["timew", "annotate", "@1", new_annotation])
+
+
+if __name__ == "__main__":
+ old = json.loads(input_stream.readline().decode("utf-8", errors="replace"))
+ new = json.loads(input_stream.readline().decode("utf-8", errors="replace"))
+ print(json.dumps(new))
+ main(old, new)
diff --git a/modules/by-name/ti/timewarrior/taskwarirror_hooks/on-modify_track-total-active-time.py b/modules/by-name/ti/timewarrior/taskwarrior_hooks/on-modify.track-total-active-time.py
index d5b380d0..303a5c57 100755
--- a/modules/by-name/ti/timewarrior/taskwarirror_hooks/on-modify_track-total-active-time.py
+++ b/modules/by-name/ti/timewarrior/taskwarrior_hooks/on-modify.track-total-active-time.py
@@ -1,8 +1,17 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
+# nixos-config - My current NixOS configuration
+#
# Copyright (C) 2016-present Arctic Ice Studio <development@arcticicestudio.com>
# Copyright (C) 2016-present Sven Greb <development@svengreb.de>
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
# Project: igloo
# Repository: https://github.com/arcticicestudio/igloo
@@ -20,37 +29,24 @@ By default, this plugin allows to have one task active at a time. This can be ch
``1``.
Note:
- This hook requires Python 3 and the `taskw`_ package to be installed which provides the python bindings for Taskwarrior!
- Also note that this hook is only compatible with Taskwarrior version greater or equal to 2.4!
+ Note that this hook is only compatible with Taskwarrior version greater or equal to 2.4!
This hook is a fork from `kostajh/taskwarrior-time-tracking-hook`_
-.. _taskw:
- https://pypi.python.org/pypi/taskw
.. _kostajh/taskwarrior-time-tracking-hook:
https://github.com/kostajh/taskwarrior-time-tracking-hook
"""
import datetime
import json
-import re
import sys
import subprocess
-from taskw import TaskWarrior
from typing import TypeVar
TIME_FORMAT = "%Y%m%dT%H%M%SZ"
UDA_KEY = "total_active_time"
-w = TaskWarrior(config_filename=sys.argv[4].replace("rc:", ""))
-config = w.load_config(config_filename=sys.argv[4].replace("rc:", ""))
-if "max_active_tasks" in config:
- MAX_ACTIVE = int(config["max_active_tasks"])
-else:
- MAX_ACTIVE = 1
-
-"""Compiled regular expression for the duration as ISO-8601 formatted string."""
-ISO8601DURATION = re.compile("P((\d*)Y)?((\d*)M)?((\d*)D)?T((\d*)H)?((\d*)M)?((\d*)S)?")
+MAX_ACTIVE = 1
"""The duration type either as integer (in seconds), as ISO-8601 formatted string ("PT1H10M31S") or the seconds suffixed with "seconds"."""
DurationType = TypeVar("DurationType", str, int)
@@ -63,31 +59,7 @@ def duration_str_to_time_delta(duration_str: DurationType) -> datetime.timedelta
:return: The duration as timedelta object
"""
if duration_str.startswith("P"):
- match = ISO8601DURATION.match(duration_str)
- if match:
- year = match.group(2)
- month = match.group(4)
- day = match.group(6)
- hour = match.group(8)
- minute = match.group(10)
- second = match.group(12)
- value = 0
- if second:
- value += int(second)
- if minute:
- value += int(minute) * 60
- if hour:
- value += int(hour) * 3600
- if day:
- value += int(day) * 3600 * 24
- if month:
- # Assume a month is 30 days for now.
- value += int(month) * 3600 * 24 * 30
- if year:
- # Assume a year is 365 days for now.
- value += int(year) * 3600 * 24 * 365
- else:
- value = int(duration_str)
+ value = datetime.fromisoformat(duration_str)
elif duration_str.endswith("seconds"):
value = int(duration_str.rstrip("seconds"))
else: