Simple activity tracker for Linux
Sometimes it is funny to gather some statistics about your activities - how much time you spend reading, how much time you spend programming, etc.
There is a Gnome panel app specifically for that purpose - Project Hamster. It does it's job well, but not well enough - it still requires you to manually specify start and end of each task, and I often forget to do that.
But why do we need a full-blown app for such simple thing? I really think that we can mine most of the needed information simply by analyzing the focused window properties (actually, just two of them - process, that owns that window, and title of window). For example, when the main window is from "evince" program, then I'm probably reading some book, if it is Emacs or terminal, I'm coding, if Opera - most probably reading feeds.
Window title is needed only in specific cases, when you can't differentiate based on the program executable name - sadly, most of python programs fall into this category. For example - Guake terminal - the program name is "python" yet the window title is "Guake!".
It turns out such activity tracker program is shorter than this blog post itself :)
Basically, all we need is to detect main window title and process once in a minute, and append it to the log file (which has current date as it's name).
For running that process once a minute, we'll use cron, and for title and process extraction, I used xdotool and xwininfo programs.
Actual code:
There is a Gnome panel app specifically for that purpose - Project Hamster. It does it's job well, but not well enough - it still requires you to manually specify start and end of each task, and I often forget to do that.
But why do we need a full-blown app for such simple thing? I really think that we can mine most of the needed information simply by analyzing the focused window properties (actually, just two of them - process, that owns that window, and title of window). For example, when the main window is from "evince" program, then I'm probably reading some book, if it is Emacs or terminal, I'm coding, if Opera - most probably reading feeds.
Window title is needed only in specific cases, when you can't differentiate based on the program executable name - sadly, most of python programs fall into this category. For example - Guake terminal - the program name is "python" yet the window title is "Guake!".
It turns out such activity tracker program is shorter than this blog post itself :)
Basically, all we need is to detect main window title and process once in a minute, and append it to the log file (which has current date as it's name).
For running that process once a minute, we'll use cron, and for title and process extraction, I used xdotool and xwininfo programs.
Actual code:
#!/bin/bash
# since we are launching from cron, we need to tell it which display to use
export DISPLAY=:0.0
# and also we want to stop logging when screensaver is active
if [[ $(gnome-screensaver-command -q | head -1) =~ "inactive" ]]
then
windowid=$(xdotool getwindowfocus)
windowproc=$(ps -e | grep $(xdotool getwindowpid $windowid) | grep -v grep | awk '{print $4}')
windowtitle=$(xwininfo -root -children | grep $(printf '%x\n' $windowid) | grep -oEi '"[^"]+"' | head -1)
echo $windowproc $windowtitle >> ~/log/activity/`date +%y-%m-%d`.log
fi
Now, just add this line to your /etc/crontab - and the tracker is finished!
* * * * * username /path/to/that/logging/script.sh
For analysis, I just used a quick scala script:
#!/usr/bin/scala
!#
val activities = Seq(
("blogging", "chrome", "^Blogger".r),
("games", "chrome", "Game".r),
("guake", "python", "^Guake!$".r)
)
io.Source.fromFile(args(0)).getLines.toList // read the file
.map(_.span(' '!=)).map(t => (t._1, t._2.trim.drop(1).reverse.drop(1).reverse)) // extract process-title pairs
// convert those specific activities
.map { case (prog, title) =>
activities.find { case (act, exec, regex) => prog == exec && regex.findFirstIn(title).isDefined }
.map(_._1).getOrElse(prog)
}
.groupBy(a=>a).toList
.map(t => (t._1, t._2.size))
.sortWith(_._2 > _._2)
.foreach { p => println("%4d %s" format(p._2, p._1)) }
That's it - now I can clearly see how much of my time is going down to trash can. And, as you all know, "what gets measured, gets improved" :)
Here there is another good and simple script solution, https://askubuntu.com/a/780542/1175982
ReplyDeleteBtw there is "arbtt" as well
I must confess that in the years passed since this blog post I moved from "automatic" tracking towards "manual", where I start/stop the timers manually. This allows storing information with much more granularity (at the expense of much more hassle, of course).
DeleteBut maybe it will be useful to augment the manually collected information with some automatic stuff. "arbtt" looks like a solid choice, thanks for the suggestion!