import os
import time
import subprocess
import logging
import json
import structpack
from collections import namedtuple

IDLE_TIME = 15
APP_TIME = 1

log = logging.getLogger()

logging.basicConfig(
    level=logging.DEBUG,
    # format='%(message)s',
    datefmt='%Y-%m-%d %H:%M:%s',
    filename='logs/window.log',
)


class Entry(structpack.msg):
    idle        = structpack.bool
    app         = structpack.str
    window      = structpack.str
    start_time  = structpack.int
    end_time    = structpack.int

    def __init__(self, idle=False, app='', window='', start_time=0, end_time=0):
        self.idle   = idle
        self.app    = app
        self.window = window
        self.start_time = int(start_time)
        self.end_time   = int(end_time)

    def __eq__(self, that):
        return \
            (self.idle, self.app, self.window) == \
            (that.idle, that.app, that.window)

    def duration(self):
        return self.end_time - self.start_time


def get_idle_time():
    import Quartz
    idle = Quartz.CoreGraphics.CGEventSourceSecondsSinceLastEventType(1, 0xFFFFFFFF)
    return idle


def get_active_window():
    line = subprocess.check_output('osascript window.scpt', stderr=subprocess.STDOUT, shell=True).strip()
    app, window = line.split(':', 1)
    return app, window


def get_entry():
    idle = get_idle_time() > IDLE_TIME
    if idle:
        app, window = ('IDLE', 'IDLE')
    else:
        app, window = get_active_window()
    return Entry(idle, app, window, time.time())


def get_entries():
    entry = get_entry()

    while 1:
        time.sleep(APP_TIME)
        entry.end_time = time.time()

        next = get_entry()
        if entry.duration() > 300:
            yield entry

        if not next == entry:
            yield entry
            entry = next



def main():
    for entry in get_entries():
        log.info('Entry:' + json.dumps(entry.pack()))


if __name__ == '__main__':
    main()




















@@ -60,16 +60,27 @@
 rt json%0A
+import sys%0A
 import s
@@ -117,18 +117,19 @@
 ort 
-namedtuple
+defaultdict
 %0A%0AID
@@ -1258,16 +1258,29 @@
 strip()%0A
+    try:%0A    
     app,
@@ -1308,16 +1308,112 @@
 ':', 1)%0A
+    except:%0A        import traceback%0A        traceback.print_exc()%0A        return 'ERROR', line%0A
     retu
@@ -1849,16 +1849,41 @@
 ld entry
+%0A            entry = next
 %0A%0A      
@@ -1961,17 +1961,15 @@
 t%0A%0A%0A
-%0A
 def 
-main
+log
 ():%0A
@@ -2060,43 +2060,1079 @@
 )%0A%0A%0A
-if __name__ == '__main__':%0A    main
+def load_entries(inp):%0A    for line in inp:%0A        entry_json = line.split('Entry:', 1)%5B1%5D%0A        entry_data = json.loads(entry_json)%0A        entry = Entry.load(entry_data)%0A        yield entry%0A%0A%0Adef display_time(d, t):%0A    h = d / 60 / 60%0A    m = d / 60 %25 60%0A    p = d * 100.0 / t%0A    return '%252dh %252dm (%252d%25%25)' %25 (h, m, p)%0A%0A%0Adef stats(entries):%0A    time = defaultdict(lambda:defaultdict(int))%0A    app_time = defaultdict(int)%0A    total = 0%0A    for e in entries:%0A        d = e.duration()%0A        total += d%0A        app_time%5Be.app%5D += d%0A        time%5Be.app%5D%5Be.window%5D += d%0A%0A    for app, d in sorted(app_time.items(), reverse=True):%0A        if d %3C 5 * 60:%0A            continue%0A        print '### %25-50s ### %25s' %25 (app, display_time(d, total))%0A        for window, d in sorted(time%5Bapp%5D.items(), reverse=True):%0A            if d %3C 60:%0A                continue%0A            print '        %25-50s %25s' %25 (window%5B:50%5D, display_time(d, total))%0A        print%0A%0A%0A%0Aif __name__ == '__main__':%0A    if len(sys.argv) == 2:%0A        stats(load_entries(file(sys.argv%5B1%5D)))%0A%0A    else:%0A        log
 ()%0A%0A

fix bug, add basic stats