From: Colin Watson Date: Sun, 23 Feb 2025 20:08:34 +0000 (+0000) Subject: Qalculate time hacks X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~cjwatson/git?a=commitdiff_plain;h=12ff32a121423976ef65fd13e0e00534a1f8f44d;p=blog.git Qalculate time hacks --- diff --git a/content/qalculate-time-hacks.rst b/content/qalculate-time-hacks.rst new file mode 100644 index 00000000..25d2d075 --- /dev/null +++ b/content/qalculate-time-hacks.rst @@ -0,0 +1,167 @@ +Qalculate time hacks +==================== + +:slug: qalculate-time-hacks +:date: 2025-02-23 20:00:12 +00:00 +:category: debian +:tags: columbiform, debian, planet-debian, planet-ubuntu +:mastodon-id: 114055000943801013 + +Anarcat recently `wrote +`__ about `Qalculate +`__, and I think I'm a convert, even though +I've only barely scratched the surface. + +The thing I almost immediately started using it for is time calculations. +When I `started tracking my time <{filename}/task-management.md>`__, I +quickly found that `Timewarrior `__ was good at +keeping all the data I needed, but I often found myself extracting bits of +it and reprocessing it in variously clumsy ways. For example, I often don't +finish a task in one sitting; maybe I take breaks, or I switch back and +forth between a couple of different tasks. The raw output of ``timew +summary`` is a bit clumsy for this, as it shows each chunk of time spent as +a separate row: + +.. code-block:: console + + $ timew summary 2025-02-18 Debian + + Wk Date Day Tags Start End Time Total + W8 2025-02-18 Tue CVE-2025-26465, Debian, 9:41:44 10:24:17 0:42:33 + next, openssh + Debian, FTBFS with GCC-15, 10:24:17 10:27:12 0:02:55 + icoutils + Debian, FTBFS with GCC-15, 11:50:05 11:57:25 0:07:20 + kali + Debian, Upgrade to 0.67, 11:58:21 12:12:41 0:14:20 + python_holidays + Debian, FTBFS with GCC-15, 12:14:15 12:33:19 0:19:04 + vigor + Debian, FTBFS with GCC-15, 12:39:02 12:39:38 0:00:36 + python_setproctitle + Debian, Upgrade to 1.3.4, 12:39:39 12:46:05 0:06:26 + python_setproctitle + Debian, FTBFS with GCC-15, 12:48:28 12:49:42 0:01:14 + python_setproctitle + Debian, Upgrade to 3.4.1, 12:52:07 13:02:27 0:10:20 1:44:48 + python_charset_normalizer + + 1:44:48 + +So I wrote this Python program to help me: + +.. code-block:: python + + #! /usr/bin/python3 + + """ + Summarize timewarrior data, grouped and sorted by time spent. + """ + + import json + import subprocess + from argparse import ArgumentParser, RawDescriptionHelpFormatter + from collections import defaultdict + from datetime import datetime, timedelta, timezone + from operator import itemgetter + + from rich import box, print + from rich.table import Table + + + parser = ArgumentParser( + description=__doc__, formatter_class=RawDescriptionHelpFormatter + ) + parser.add_argument("-t", "--only-total", default=False, action="store_true") + parser.add_argument( + "range", + nargs="?", + default=":today", + help="Time range (usually a hint, e.g. :lastweek)", + ) + parser.add_argument("tag", nargs="*", help="Tags to filter by") + args = parser.parse_args() + + entries: defaultdict[str, timedelta] = defaultdict(timedelta) + now = datetime.now(timezone.utc) + for entry in json.loads( + subprocess.run( + ["timew", "export", args.range, *args.tag], + check=True, + capture_output=True, + text=True, + ).stdout + ): + start = datetime.fromisoformat(entry["start"]) + if "end" in entry: + end = datetime.fromisoformat(entry["end"]) + else: + end = now + entries[", ".join(entry["tags"])] += end - start + + if not args.only_total: + table = Table(box=box.SIMPLE, highlight=True) + table.add_column("Tags") + table.add_column("Time", justify="right") + for tags, time in sorted(entries.items(), key=itemgetter(1), reverse=True): + table.add_row(tags, str(time)) + print(table) + + total = sum(entries.values(), start=timedelta()) + hours, rest = divmod(total, timedelta(hours=1)) + minutes, rest = divmod(rest, timedelta(minutes=1)) + seconds = rest.seconds + print(f"Total time: {hours:02}:{minutes:02}:{seconds:02}") + +.. code-block:: console + + $ summarize-time 2025-02-18 Debian + + Tags Time + ─────────────────────────────────────────────────────────────── + CVE-2025-26465, Debian, next, openssh 0:42:33 + Debian, FTBFS with GCC-15, vigor 0:19:04 + Debian, Upgrade to 0.67, python_holidays 0:14:20 + Debian, Upgrade to 3.4.1, python_charset_normalizer 0:10:20 + Debian, FTBFS with GCC-15, kali 0:07:20 + Debian, Upgrade to 1.3.4, python_setproctitle 0:06:26 + Debian, FTBFS with GCC-15, icoutils 0:02:55 + Debian, FTBFS with GCC-15, python_setproctitle 0:01:50 + + Total time: 01:44:48 + +Much nicer. But that only helps with some of my reporting. At the end of a +month, I have to work out how much time to bill Freexian for and fill out a +timesheet, and for various reasons those queries don't correspond to single +``timew`` tags: they sometimes correspond to the sum of all time spent on +multiple tags, or to the time spent on one tag minus the time spent on +another tag, or similar. As a result I quite often have to do basic +arithmetic on time intervals; but that's surprisingly annoying! I didn't +previously have good tools for that, and was reduced to doing things like +``str(timedelta(hours=..., minutes=..., seconds=...) + ...)`` in Python, +which gets old fast. + +Instead: + +.. code-block:: console + + $ qalc '62:46:30 - 51:02:42 to time' + (225990 / 3600) − (183762 / 3600) = 11:43:48 + +I also often want to work out how much of my time I've spent on Debian work +this month so far, since Freexian pays me for `up to 20% of my work time on +Debian `__; if I'm +under that then I might want to prioritize more Debian projects, and if I'm +over then I should be prioritizing more Freexian projects as otherwise I'm +not going to get paid for that time. + +.. code-block:: console + + $ summarize-time -t :month Freexian + Total time: 69:19:42 + $ summarize-time -t :month Debian + Total time: 24:05:30 + $ qalc '24:05:30 / (24:05:30 + 69:19:42)' + (86730 / 3600) / ((86730 / 3600) + (249582 / 3600)) ≈ 0.2578855349 + +I love it. diff --git a/posted_on_Mastodon.txt b/posted_on_Mastodon.txt index 73957f8a..e6a6d0c4 100644 --- a/posted_on_Mastodon.txt +++ b/posted_on_Mastodon.txt @@ -64,6 +64,7 @@ pipeline-library.html playing-with-rich.html porting-ghc-a-tale-of-two-architectures.html python-sigpipe.html +qalculate-time-hacks.html quality-in-12-04.html re-signing-ppas.html reply-perl-is-strange.html