org-fit or what it means to work on the utterly useless
Table of Contents
1. background
Over the course of late 2022 and early 2023, I started to integrate more and more data of my daily life into org mode. This included last.fm scrobbles, my YouTube watch history, train and bus rides and the topic of this entry, going outside, touching grass, even.
2. idea
Despite the questionable consequences for my privacy, I’ve been using Google Fit for a while now, though mostly as a basic step counter and movement time tracker. Since I was using the org mode clock way more extensively back then, my plan was to incorporate the Google Fit activity data into the clock times of a specific task.
2.1. failed attempt
Since this is essentially a tool for Emacs, my first idea was to write the entire thing in Emacs Lisp. This most importantly includes the Google API calls and authentification steps, which follow the standard OAuth2 protocol. Now while there are Emacs implementations, I couldn’t the one of my choice working no matter the effort, since some step of the authentification simply failed no matter what I did, it seems to be a problem in the flow of the library.
2.2. the two-layer solution
So I instead checked for Haskell implementations of the OAuth2 flow, and indeed found a specific repository that addressed the Google API auth flow. Because I’m absolutely horrible at package management, I decided not to use the package directly, but rather just fork the repository and change the example app in a truly cursed act of desperation. Getting this to a working state still took quite a lot of effort, so the details of the tool are still worth explaining a little. After sending the request, a decently fucked up JSON parsing bit is required to simplify the data - in both of these steps, the conversion of 3 different date formats (nanoseconds since epoch for the Google API, half a dozen of different date datatypes for Haskell, not entirely standard conforming UTC timestamps for Org mode) proved to be the most difficult step. From there on, all that the program does is print the Org clock lines and ignore activities that are too short or of type 0 (driving), to not clutter the clock report.
Of course this wasn’t the end of the story, one still needs an Emacs Lisp layer to actually add the
generated data to the Org logbooks in question. Enter org-fit, an almost as confusingly named package
that’s not even that hacky and even follows a proper packaging standard, too.
Its utility to the user is rather limited - it provides the function org-fit-update, which by default
runs the get-fit
tool from the Haskell stage and passes the current time as an argument.
The heading given by the customizable ID is then found using the handy org-id. If any lines are
generated by the tool, they’re added to the logbook drawer and the tool updates a property indicating
the time the last update occurred. This is done by jumping to the headline in question, searching for
the logbook drawer via regexp1, then iteratively inserting the data and having org mode update the
clocking durations, too. In a somewhat early stage, I even tried to add seconds to the clock times (because why
even bother with data that isn’t as exact as it possibly could be, right?), but it turned out Org mode
does not handle this well, sadly, so I had to resort to minutes.
As I had no prior experience in writing “real” Emacs lisp, the whole concept of marker mangling, save and buffer excursion strategies took me a while to get used to - it’s a style of string manipulation that diverges a lot from “modern” programming, but it turned out to be really interesting and definitely worth exploring more.
3. result
This whole setup works out really well - by using run-with-timer, the file automatically updates once
an hour without any problems. However, after a few days of inactivity per computer (which occurs decently
frequently, since I use both a desktop PC and a notebook), the API credentials run stale and I have to
manually run get-fit
in a terminal to initiate the auth process once again. At least, the update time
is not changed since org-fit
doesn’t detect any output, so the data gets retrieved at a later point.
There’s probably a way to do this directly within Emacs, but I honestly can’t be bothered.
The main problem is my recent lack of interest - while it’s still nice to collect this data, I tend to use the clock function much less than I once did, so it’s more of a curiosity than anything else now, then again, that seems to be the point of most of my weirdly specific projects to begin with. hm.
Footnotes:
not to be confused with regex! Emacs regexp is by absolutely no means the same thing and conforms to pretty much no standards at all, sadly.