Posted on Fri 16 March 2012

Life-logging for your computer usage

Have you ever wondered where most of your time goes when using your computer? I always did, so I wrote a small script which logs the currently visible and focused apps:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
#!/usr/bin/python
import json
import os
import time
import subprocess

visible_workspaces = []
filtered = [                                # filter everything which is not a window
    "root", "LVDS", "DFP1",                                                         # monitors
    "i3bar for output LVDS", "i3bar for output DFP1",       # i3bar
    "topdock", "content", "bottomdock",                                 # docks
    "1", "2", "3", "4", "5", "6", "7", "8", "9", "10",  # workspace numbers
    "#00FF00", "#aa00aa", "#00aa00"                                         # window title bar colors
    ]
clean_mapping = {                       # strings to recognize apps by and the correct app name
    "XChat": "XChat",
    "Sublime": "Sublime Text 2",
    "Chrome": "Google Chrome",
    "Skype": "Skype",
    "VLC": "VLC",
    "calibre": "calibre",
    "GNU Image Manipulation": "GIMP",
    "Buddy List": "Pidgin",
    "jabber": "Pidgin",
    "pts": "Terminal",
    "Defender's Quest": "Defender's Quest",
    "Desura": "Desura"
    }

visible_apps = []
focused_apps = []

# i3's windows are contained in a tree structure, so we need to parse that
def parse_node(node):
    if "num" in node and node["num"] not in visible_workspaces:
        return

    name = node["name"]
    if name not in filtered:
        visible_apps.append(name)
        if node["focused"] == True:
            focused_apps.append(name)

    for n in node["nodes"]:
        parse_node(n)

# clean up window titles
def clean_name(name):
    for key in clean_mapping:
        if key in name:
            return clean_mapping[key]
    return name


screensaver_enabled = False
try:
    screensaver_enabled = "non" not in subprocess.check_output(
        "xscreensaver-command -time 2> /dev/null", shell=True)
except:
    pass

workspaces = json.loads(
    subprocess.check_output(
        "i3-ipc -j -t 1", shell=True))

for w in workspaces:
    if w["visible"] == True:
        visible_workspaces.append(int(w["name"]))

apps = json.loads(subprocess.check_output(
    "i3-ipc -j -t 4", shell=True))

parse_node(apps)

visible_apps = [clean_name(n) for n in visible_apps]
focused_apps = [clean_name(n) for n in focused_apps]

output_template = """{ "time": %d, "visible": %s, "focused": %s }"""
if screensaver_enabled:
    focused_apps = ["screensaver"]

print (output_template % 
    (time.time(), visible_apps, focused_apps)
    ).replace("'", "\"")

It uses the IPC interface to communicate with [cached]i3, the tiling window manager I use. (if you don't already use a tiling wm: I highly recommend it!) Communication also relies on the [cached]i3-ipc ruby library, which you'd have to install to use my script.

Output looks like this:

1
{ "time": 1331935529, "visible": ['Terminal', 'Sublime Text 2'], "focused": ['Terminal'] }

The current time in seconds, a list of all visible windows and finally a list of the currently focused windows. Each line is one json object, so you can easily pare it. I use cron to run this script every minute and log it's ouput to a file:

1
2
# m h  dom mon dow   command
  * *  *   *   *     export DISPLAY=:0 && /home/mononofu/.app_usage.py >> /home/mononofu/.app_usage.log

It's important to set the DISPLAY variable or the script won't work! I'll publish a script for easy analysis of the data in the next few days, so stay tuned.

Tags: life

© Julian Schrittwieser. Built using Pelican. Theme by Giulio Fidente on github. .