For the English part of the blog, see Content AND Presentation.
A few weeks ago I was desiging some time-related code, and that code needed to handle “events” happening on various days. Without getting into too much detail, it had to do various things depending on stuff like “did this event happen at least a week after another one” or “how many days after «day zero» did that event happen”. Since there were quite a few rules concerning that, I wanted to document them in a clear, visual way.
I decided that a “timeline diagram” would be a perfect way to visualize these sorts of relationships. Of course, I didn’t want to draw my diagrams on paper and scan them (well, this is actually a viable option – pencil and paper are hard to beat as a UI! – just not what I wanted), so I needed to select some tool. There are many, many tools which can help with that – my teammates like Excalidraw a lot, there are many others, both web-based and desktop-based. Me being me, I decided to use Emacs and ASCII art. While I would definitely not call myself an “artist”, I have a sort of history of drawing stuff in Emacs with ASCII characters. Let’s add to that history!
(defun insert-time-axis ()
"Insert a \"time axis\" at point."
(interactive)
(let ((points ()) point (max 0))
(while
(progn
(setq point (read-number
"Time point in days (0 to finish): "))
(push point points)
(when (> point max)
(setq max point))
(plusp point)))
(setq points (sort points))
(unless (bolp)
(insert "\n"))
(dotimes (i (1+ max))
(insert
(if (memq i points)
"| " " ")))
(insert "\n")
(dotimes (i (1+ max))
(insert
(if (memq i points)
"+-" "--")))
(insert "->\n")
(dotimes (i (1+ max))
(insert
(if (memq i points)
(format "%-2d" i) " ")))
(insert " time [days]\n")))
As you can see, this code is exceptionally crude – it’s written in a very imperative style (and there’s nothing wrong with that in this particular case!), with all the setqs and dotimes loops. Also, instead of accepting a list of integers and some clever trickery in the interactive clause, it just doesn’t accept any arguments and uses read-number directly in the body of the function to get the list of numbers to mark with “ticks”. This means my command cannot be used non-interactively – but that’s ok, since I don’t intend to ever use it that way!
I can now say M-x insert-time-axis and type a few numbers, ending them with a zero (which makes sense – I always want a zero on my time axis) and get a “picture” like this:
| | | | +-------------+-----+-------+--> 0 7 10 14 time [days]
I can then manually edit it to add descriptions to the days marked with ticks etc., for example:
one week
| ten days
start | | two weeks
| | | |
+-------------+-----+-------+-->
0 7 10 14 time [days]
Was it faster to type this command (and make a few editions to the diagram itself) than to learn enough Excalidraw to make this diagram? Maybe, maybe not. Is it more cool to have a little command to create ASCII art timeline diagrams like this? Definitely. Would it be much more complicated to code automatic support for tick labels like the ones I added manually above? A bit, yes. Would it be difficult to write a command to create other diagrams programmatically, say Venn diagrams, or graphs of functions, or flowcharts etc.? Not really – if the diagram is too complex to draw using just plain inserts, I can always resort to calling Artist mode functions like I did several times before.
By the way, in the old days when I worked in academia and used LaTeX extensively, I would have probably used TikZ for diagrams like this. (I very highly recommend reading the section titled “Guidelines on Graphics” in its documentation, even if you do not plan to use TikZ or LaTeX at all – it is a solid collection of very no-nonsense tips on creating graphics in whichever tool you like, aimed especially at scientific or engineering documents.) And in fact, TikZ can create graphics in svg format, so it can be used for web-based documents, too. (Now that I think about it, I might be tempted to write another blog post showing how to draw similar timeline diagrams using TikZ and how to generate them in svg format. We’ll see!) In the meantime, if you ever write an engineering document requiring diagrams of any sort, remember that using Emacs – either interactively, using Picture mode or Artist mode, or programmatically – to draw them with ASCII art is a perfectly viable option.
Once I wrote about what I called “Org mode burst timer”, another idea occurred to me. That code is good, and may be useful for some people – if it is for you, feel free to use it! But I can do something better for me. Instead of requiring myself to set the burst property manually, I could tie this code to my Emacs Beeminder client. When a clock is started on a beeminded task, it could retrieve the necessary data about the goal associated with the current headline and notify me when I work on it enough.
(Note: the following requires some knowledge about how Beeminder works, and in particular it uses a bit of Beeminder jargon.)
Now, this could work in several ways. The simplest way would be just to notify me after I work on a task for an amount of time equal to my daily goal, no matter how much I have worked on it today. For example, assume that I have my writing goal set up to write for 20 minutes per day, I have one day of buffer, I need to write for 17 minutes to increase the buffer to two days, and I’ve already been writing for 15 minutes today. With this approach the timer would be set to 20 minutes anyway. On the other hand, one could argue that the “correct” way would be to set it to 5 minutes (since I’ve already done 15 today), or even 2 (since this is what I need to increase the safety buffer). It is very tempting to use the first, simplest approach, but I decided to go a bit fancy and use a mixture of the second and third ones. I want to be notified as soon as the amount of time I have worked on a goal today is equal to my daily rate. Additionally, if my goal is on a beemergency, I want to be notified as soon as I did enough to dispatch it.
So, I decided to add such a feature to my Beeminder client. I wrote most of it in 2015 and 2016 when I was a beginning Elisp programmer, and many choices I made back then are… less than ideal, so to speak. (For example, it uses eval in some places.) Coming back to code you wrote 10 years ago is painful in more than one way – not only do you see your stupidity, but you have to collaborate with your stupid self from 10 years ago. But I really want to have this feature, so I did it anyway.
I’ve introduced two options. First of all, beeminder-org-notify-when-enough-p. Setting this to non-nil (which is the default now) will make Emacs notify me when the amount of work I’ve done on the goal is equal to the daily rate. Additionally, there is beeminder-org-notify-when-buffer-increased, firing a notification when I’ve done enough to increase my safety buffer by one day. This can be set to nil (to disable that feature), the symbol beemergency (the default, to enable it only for eep days) or anything else (to always enable it). Finally, I introduced beeminder-org-notification-sound, which has the same semantics as org-clock-sound (and is set to t by default).
And that’s it for today. If you happen to use my Emacs Beeminder client, you can download the newest version from GitHub and start using these options now. (Note that that version also includes a few other minor updates, the most visible being that the “Beeminder goal details” window shows a few aggregates – by default, the sum, median, average and daily average of the displayed datapoints. See beeminder-goal-template-fields-alist if you want to add yours.) At least for me, they seem to be quite helpful (even if a tiny bit distracting). While at that, I fixed some minor issues in the code (mainly adding necessary options to a few defcustoms), so even if you don’t want to use the new features, you might want to upgrade.
CategoryEnglish, CategoryBlog, CategoryEmacs, CategoryOrgMode
Org mode has two built-in timers – the “relative timer” (which is basically a stopwatch – it starts with 0 and counts up), useful for taking meeting minutes with an indication of when things were discussed, and a “countdown timer” (which is, well, a countdown timer – you tell it a duration, it counts down from it, and rings a bell when it gets to zero).
What I sometimes miss is the following feature. I’d like to have some kind of timer which would tell me when a given amount of time passed, but then it would keep running. For instance, let’s assume that I want to write this blog post for at least 25 minutes, but also measure the time I spend doing it even if I keep writing for longer.
Of course, this is more or less what my tomato.el did from early on (and what I partially recreated with ketchup.el). Interestingly, Org mode has a very similar feature built-in. When you set an effort estimate on a task and clock it, you’ll get notified after reaching your estimate. However, this is not exactly what I need – I’d like to have a similar feature but also for “bursts of work”. I have some long-running tasks for which the effort estimate doesn’t make sense – for example, when I read a book, I often track the time I spend on it. On the other hand, I usually want to read said book for at least N minutes per day (of course, those who read my blog regularly will guess that this is tied to Beeminder), and it’s best to spend that time in one session.
So, let’s get working on this. I’m going to write some code which will be tied to the Org mode clocking mechanism, and if a clock is started on a task which has a burst property, it will start a timer to notify me that many minutes later. (I don’t need my feature to display how much time has passed since I started the clock, since I have org-clock-mode-line-total set to current.) This could be a global minor mode, but since I do not envision turning this feature off, and it will only influence anything when a very specific property is set, I’m fine with just having it on all the time.
(defvar burst--timer nil
"Timer for bursts of work.")
(defun burst-run-timer ()
"Run the burst timer."
(when-let* ((burst-string (org-entry-get (point) "burst" t))
(burst (string-to-number burst-string)))
(setq burst--timer
(run-with-timer (* burst 60)
nil
#'org-notify
(format "%s minutes have passed"
burst)
org-clock-sound))))
(defun burst-cancel-timer ()
"Cancel the burst timer."
(when (timerp burst--timer)
(cancel-timer burst--timer)))
(add-hook 'org-clock-in-hook #'burst-run-timer)
(add-hook 'org-clock-out-hook #'burst-cancel-timer)
(add-hook 'org-clock-cancel-hook #'burst-cancel-timer)
In fact, the code is very similar to the one I wrote for my ketchup feature. The difference is in the condition used to start the timer. I am wondering if this means that an abstraction for “run a timer when clocking in, cancel it when clocking out or canceling the clock” could be useful.
Anyway, that’s it for today – although while writing this, I had an even better idea, so expect a follow-up to this post soon!
CategoryEnglish, CategoryBlog, CategoryEmacs, CategoryOrgMode