#!/usr/bin/env python # Author: Josh Holtrop # Simple script to show me my week's hours as far as my Ubuntu box knows them import os import sys import re import argparse from datetime import * LOG_FILE = '/var/log/auth.log' ADJUSTMENTS_FILE = os.path.expanduser('~/.hours') ISO_DATE_FMT = '%Y-%m-%d' now = datetime.now() monday = (now - timedelta(now.weekday())).date() def get_date_from_day_spec(day_spec): if re.match(r'\d+', day_spec): for i in range(7): d = monday + timedelta(i) if d.day == int(day_spec): return d.strftime(ISO_DATE_FMT) return '' days = { 'mon': 0, 'tue': 1, 'wed': 2, 'thu': 3, 'fri': 4, 'sat': 5, 'sun': 6 } day_spec = day_spec.lower() if len(day_spec) > 3: day_spec = day_spec[:3] if day_spec in days: return (monday + timedelta(days[day_spec])).strftime(ISO_DATE_FMT) return '' def get_adjustments(): adjustments = {} if not os.path.isfile(ADJUSTMENTS_FILE): return adjustments f = open(ADJUSTMENTS_FILE, 'r') while True: line = f.readline() if line == '': break m = re.match(r'adj\s+(\S+)\s+(\S+)', line) if m is not None: adjustments[m.group(1)] = float(m.group(2)) f.close() return adjustments def save_adjustments(adjustments): f = open(ADJUSTMENTS_FILE, 'w') for a in adjustments: if datetime.strptime(a, ISO_DATE_FMT).date() >= monday: if adjustments[a] != 0.0: f.write('adj %s %f\n' % (a, adjustments[a])) f.close() def get_dt_from_log_line(line): m = re.match(r'(\S\S\S)\s+(\d+)\s+(\d+):(\d+):(\d+)\s+.*', line) if m is not None: month_name, day, t_hr, t_min, t_sec = m.group(1, 2, 3, 4, 5) try: month = datetime.strptime(month_name, '%b').month except: return None year = now.year if month == 12 and now.month == 1: year -= 1 dt = datetime(*map(int, [year, month, day, t_hr, t_min, t_sec])) return dt return None def main(argv): goal_hours = 40 adjustments = get_adjustments() parser = argparse.ArgumentParser('hours') parser.add_argument('-t', '--total', type=float, metavar='TOT', help='set target number of hours for the week') parser.add_argument('-a', '--adjust', type=float, metavar='ADJ', help="adjust given day's (default today) hours by ADJ") parser.add_argument('-d', '--day', help='specify which day to adjust hours for with --adj') args = parser.parse_args(argv[1:]) if args.total is not None: goal_hours = args.total if args.adjust is not None: adj_date = now.strftime(ISO_DATE_FMT) if args.day is not None: adj_date = get_date_from_day_spec(args.day) if adj_date == '': sys.stderr.write('Unknown DAY format.\n') sys.stderr.write('Specify DAY as an integer or as a day name\n') sys.exit(2) if adj_date in adjustments: adjustments[adj_date] += args.adjust else: adjustments[adj_date] = args.adjust save_adjustments(adjustments) sys.stdout.write('Adjusted %s by %.1f\n' % (adj_date, args.adjust)) times = [] for i in range(7): times.append([None, None]) f = open(LOG_FILE, 'r') while True: line = f.readline() if line == '': break if re.search(r'gnome-screensaver.*unlocked.login.keyring', line): # found a login line dt = get_dt_from_log_line(line) idx = (dt.date() - monday).days if times[idx][0] is None or dt < times[idx][0]: times[idx][0] = dt elif re.search(r'lock-screen:', line): # found a logout line dt = get_dt_from_log_line(line) idx = (dt.date() - monday).days if times[idx][1] is None or dt > times[idx][1]: times[idx][1] = dt f.close() now_idx = (now.date() - monday).days if times[now_idx][1] is None or now > times[now_idx][1]: times[now_idx][1] = now def fmt_time_dt(dt): s = dt.strftime('%I:%M') + dt.strftime('%p')[0].lower() if s[0] == '0': s = s[1:] return s border = lambda: sys.stdout.write('-' * 40 + '\n') total_hours = 0 border() for time in times: if time[0] is not None: sys.stdout.write('%-14s' % (time[0].strftime('%d %A') + ':')) sys.stdout.write(fmt_time_dt(time[0])) sys.stdout.write(' - ') if time[1] is not None: sys.stdout.write(fmt_time_dt(time[1])) seconds = (time[1] - time[0]).seconds hours = round(seconds / 60.0 / 60.0, 1) iso_spec = time[1].strftime(ISO_DATE_FMT) if iso_spec in adjustments: hours += adjustments[iso_spec] sys.stdout.write(' (%.1f hours)' % hours) if iso_spec in adjustments and adjustments[iso_spec] != 0.0: sys.stdout.write(' [%.1f]' % adjustments[iso_spec]) total_hours += hours else: sys.stdout.write('???') sys.stdout.write('\n') border() sys.stdout.write('Total: %.1f hours' % round(total_hours, 1)) if total_hours < goal_hours: out_time = now + timedelta(0, (goal_hours - total_hours) * 60 * 60) sys.stdout.write('; %.1f at: %s %s' % (goal_hours, out_time.strftime('%a %d'), fmt_time_dt(out_time))) sys.stdout.write('\n') if __name__ == "__main__": sys.exit(main(sys.argv))