mbork / beeminder.el

A Beeminder client for Emacs
GNU General Public License v3.0
29 stars 3 forks source link

+TITLE: Beeminder client for Emacs

+AUTHOR: Marcin ‘mbork’ Borkowski

+EMAIL: mbork@mbork.pl

A Beeminder client for Emacs.

However, their default web interface sucks, and while the official Android app is a lot better, it still has a few drawbacks. The biggest one is that it is not integrated with Emacs. This Elisp library fixes that problem (and a few others, like lack of goal sorting and filtering).

This library is still under development, and everything (including keybindings) may change.

You need to install packages =request= and =anaphora= (e.g. using the built-in Emacs package manager) in order to use =beeminder.el=.

You can filter goals by derailment time (=NUMBER-OF-DAYS f d=, number of days defaults to 3), by how much time is left to the goal’s “midnight” (=HOURS f u=, defaults to 8), or by how much percent of daily rate you did today (=PERCENTAGE f t=, defaults to 100). What “today” means is governed by options =beeminder-when-the-day-ends= (which see) and =beeminder-use-goal-midnight-today-values= (which see). TL;DR: with the default setting, “today” is the stretch of time between 6:00am and 5:59am the next day. With these options, you can change that hour /or/ make the notion of “today” depend on the goal’s “midnight” setting.

You can also set a zero or negative argument to any filtering command. Try it to see what happens; =beeminder.el= tries to do the right thing for any filter.

(Currently, you can use just =d=, =u= and =t= keybindings for filtering by derailment time, urgency and percentage of today's work done. However, this may change in the future.)

You can also “kill” individual goals, i.e., make them invisible, with =f k=. (This is also bound to =C-k= for convenience). You can “unkill” all killed goals with =f y= (or =C-y=), or show (in the minibuffer) which goals are killed with =C-u f y=.

If you're like me, many of your goals are of the “do this every day” category. I usually set the rate for such goals to 0.8 daily, and enter 1.0 each day, and set the “max safe days” to e.g. 3 (of course, you need /Plan Bee/ for that). This way, I have a bit of leeway – I can safely slip once every six days. On the other hand, if one of these goals derails in, say, 2 days, it can easily get lost, especially when filtering out all goals with due date e.g. later than tomorrow. In order to avoid that, you can set the variable =beeminder-everyday-goals-list= in your =init.el= to a list of slugs of “everyday” goals (as symbols). These goals will be shown even if their deadline is later than the derailment time filter setting. You may toggle displaying them by pressing =e=.

You might also want to /save/ current filter settings for later retrieval. This can be done with =f s=. Saved goals can be retrieved by =f r=. While saving is not persistent across Emacs sessions, you can (ab)use this feature to have your favorite filter settings enabled for retrieval in =init.el= by defining the variable =beeminder-saved-filters=. For instance, to be able to quickly retrieve the goals which are derailing today, with the exception of two of them, you can put this in your =init.el=:

+BEGIN_SRC elisp

(setq beeminder-saved-filters '((killed uvi meta) (losedate . 0)))

+END_SRC

You can disable all filters with =f c=. (This also saves the current filters if no filter settings were saved previously.) Alternatively, you can disable a particular filter with =- KEY= (that is, minus sign and the key which enables that filter).

It may happen that the goals which should lose their /dirtiness/ do not do it. (One situation when it can happen is when you submit a datapoint of 0.) In such cases, you can call =M-x beeminder-clear-dirty-goals= to manually reset the “dirty” flag for all goals.

The option =beeminder-history-length= determines how many datapoints are downloaded from the server. Its default value is 7, which means a week's worth of them. Pressing =m= downloads more datapoints (with a positive prefix argument, it downloads that many more days' worth of datapoints; with a negative prefix argument, it downloads datapoints from number of days equal to the abolute value of the argument; with prefix argument equal to zero, it downloads all datapoints; without a prefix argument, it downloads datapoints from =beeminder-history-length= more days than displayed currently.)

You can press =q= or =TAB= again to quit the goal details window. Pressing =n= and =p= will move you to the next and previous datapoint (or N datapoints forward/backward with a prefix argument; notice that you don’t need to press =C-u= to enter prefix arguments here, too).

You can also press =e= to edit the current datapoint. You will be asked about the timestamp (again, using =org-read-date= if available), the value and the comment. In all three cases, the default is the previous value; for the comment, you can also use the usual minibuffer history commands like =M-n=, =M-p= or =M-r= (see the node on /Minibuffer history/ in the Emacs manual). Pressing =C-g= at any moment cancels the editing.

Pressing =d= deletes the current datapoint. Emacs will ask for confirmation; use the option =beeminder-confirm-datapoint-deletion= to change this behavior.

Note that editing a datapoint does /not/ mark the goal as dirty; the current design of dirtiness makes it rather hard to fix. Deleting a datapoint works properly in this regard.

There is (rather experimental) support for displaying graphs. Press =i= to download and view the graph for the current goal.

Pressing =W= opens the current goal in a browser.

Then, for each item you want to link to a Beeminder goal, set its =beeminder= property to =done= or =clock=, and its =slug= property to the goal slug. You might also want to set the =beeminder-org-inherit-beeminder-properties= option to =t= to turn property inheritance on for Beeminder-related stuff. (This is probably most useful for clocking subtasks.)

If for some reason you want to confirm the submitting each time, you may set the =comment= property to =ask=. Then, you will be asked for a comment each time. Other possible values for the =comment= property are: =time= (you will get a comment of the form =via Org mode at

** Marking items as DONE Marking an Org heading as DONE can automatically submit a Beeminder datapoint. For that, set the property =beeminder= to =done= and put the goal slug in the =slug= property. The amount of the datapoint will be 1, though this can be overriden by setting the property =amount= to a number.

This feature probably makes the most sense for items scheduled with a repeater.

** Submitting time for clocked items Another way of leveraging Org-mode's features is submitting time of clocked items for “do X for at least Y minutes”-type goals. For that, set the =beeminder= property to =clock= and the =slug= property to the goal slug. Each time this particular item is clocked out, the number of minutes is submitted as a Beeminder datapoint. Alternatively, you may set the =unit= property to =hours= so that the value is divided by 60.

Since it may happen that you clock out some item when offline, you may also place point at a particular clock line and trigger the submission manually by =M-x beeminder-org-submit-clock-at-point=.

If you clocked more items while you were offline, you may find the command =M-x beeminder-org-submit-all-clocks= useful. It submits all clocks in the region (if the region is active) or in the current subtree otherwise. For performance reasons, it submits only clocks that ended less than =beeminder-org-submit-all-clocks-default-minutes= minutes ago (by default 24 · 60 = 1440 minutes). This value can be also changed using a numeric prefix argument.

Note that calling =beeminder-org-submit-clock-at-point= multiple times on the same clock line submits it only once. More precisely, the idempotency key is constructed from the start and end times of the clock item.