12 [ 4, 0], [ 4, 2], [ 2, 0], [ 1, 0], [ 2, 2], [ 3, 0], [ 3, 2],
13 [ 3, 4], [ 3, 6], [ 2, 4], [ 1, 2], [ 2, 6], [ 4, 4], [ 4, 6],
17 [ 8, 0], [ 8, 2], [ 6, 0], [ 5, 0], [ 6, 2], [ 7, 0], [ 7, 2],
18 [ 7, 4], [ 7, 6], [ 6, 4], [ 5, 2], [ 6, 6], [ 8, 4], [ 8, 6],
22 [12, 0], [12, 2], [10, 0], [ 9, 0], [10, 2], [11, 0], [ ],
23 [ ], [11, 2], [10, 4], [ 9, 2], [10, 6], [12, 4], [12, 6],
27 [17, 0], [17, 2], [15, 0], [14, 0], [15, 2], [16, 0], [13, 0],
28 [13, 2], [16, 2], [15, 4], [14, 2], [15, 6], [17, 4], [17, 6],
32 [20, 0], [20, 2], [19, 0], [18, 0], [19, 2], [], [], [], [],
33 [19, 4], [18, 2], [19, 6], [20, 4], [20, 6],
37 [ ], [23, 0], [22, 2], [22, 0], [22, 4], [21, 0], [21, 2],
38 [24, 0], [24, 2], [25, 0], [25, 4], [25, 2], [26, 0], [ ],
42 def set_attr_at(j, b, n, attr, fn, val):
45 blk[attr] = fn(blk[attr], val)
47 blk[attr] = fn(None, val)
50 return cr_coord_map[row][col]
52 def set_attr(orig, new):
55 def set_bg(j, (b, n), color):
56 set_attr_at(j, b, n, "c", set_attr, color)
57 #set_attr_at(j, b, n, "g", set_attr, False)
59 def _set_tap_info(o, count, cap):
60 ns = 4 - o.count ("\n")
61 return o + "\n" * ns + "%.02f%%" % (float(count) / float(cap) * 100)
63 def set_tap_info(j, (b, n), count, cap):
64 j[b][n + 1] = _set_tap_info (j[b][n + 1], count, cap)
66 def heatmap_color (v):
67 colors = [ [0.3, 0.3, 1], [0.3, 1, 0.3], [1, 1, 0.3], [1, 0.3, 0.3]]
72 idx1, idx2 = len(colors) - 1, len(colors) - 1
74 val = v * (len(colors) - 1)
75 idx1 = int(floor(val))
77 fb = val - float(idx1)
79 r = (colors[idx2][0] - colors[idx1][0]) * fb + colors[idx1][0]
80 g = (colors[idx2][1] - colors[idx1][1]) * fb + colors[idx1][1]
81 b = (colors[idx2][2] - colors[idx1][2]) * fb + colors[idx1][2]
83 r, g, b = [x * 255 for x in r, g, b]
84 return "#%02x%02x%02x" % (r, g, b)
87 def load_keylog(fname, restrict_row):
90 with open(fname, "r") as f:
93 m = re.search ('KL: col=(\d+), row=(\d+)', line)
96 (c, r) = (int(m.group (2)), int(m.group (1)))
97 if restrict_row != None and r != int(restrict_row):
100 keylog[(c, r)] = keylog[(c, r)] + 1
104 return total / 2, keylog
110 def main(base_fn, log_fn, restrict_row = None):
112 with open(base_fn, "r") as f:
113 layout = json.load (f)
116 for row in cr_coord_map:
119 set_bg (layout, col, "#d9dae0")
120 #set_attr_at (layout, col[0], col[1], "g", set_attr, True)
122 total, log = load_keylog (log_fn, restrict_row)
125 max_cnt = max(max_cnt, log[(c, r)])
132 v = float(log[(c, r)]) / cap
133 print >> sys.stderr, "%s => %d/%d => %f = %s" % (l_flat(layout[b][n+1]), log[(c,r)], cap, v, heatmap_color(v))
134 set_bg (layout, coord(c, r), heatmap_color (v))
135 set_tap_info (layout, coord (c, r), log[(c, r)], total)
137 print json.dumps(layout)
139 if __name__ == "__main__":
140 if len(sys.argv) < 3:
141 print """Log to Heatmap -- creates a heatmap out of keyboard logs
143 Usage: log-to-heatmap.py base-layout.json logfile [row] >layout.json"""