files/hours

195 lines
6.7 KiB
Python
Executable File

#!/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'
HOURS_PER_DAY = 8.0
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')
for line in iter(f.readline, ''):
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')
parser.add_argument('-s', '--show-schedule', action='store_true',
help='show schedule for the rest of the week')
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')
for line in iter(f.readline, ''):
if (re.search(r'gnome-screensaver.*unlocked.login.keyring', line)
or re.search(r'screen unlocked', 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('-' * 55 + '\n')
total_hours = 0
sched_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)
adj = 0.0
if iso_spec in adjustments:
adj = adjustments[iso_spec]
hours += adj
sys.stdout.write(' (%.1f hours)' % hours)
if adj != 0.0:
sys.stdout.write(
' [%s%.1f]' % ('+' if adj > 0.0 else '', adj))
hours_to_do_today = min(goal_hours - total_hours, HOURS_PER_DAY)
if (iso_spec == now.strftime(ISO_DATE_FMT)
and hours < hours_to_do_today):
sched_hours += hours_to_do_today
sys.stdout.write(', %.1f at %s' % (hours_to_do_today,
fmt_time_dt(time[0] + timedelta(0,
(hours_to_do_today - adj) * 60 * 60))))
else:
sched_hours += hours
total_hours += hours
else:
sys.stdout.write('???')
sys.stdout.write('\n')
# schedule for remainder of week
if args.show_schedule:
d = now.date() + timedelta(1)
while sched_hours < goal_hours:
hours_to_do = min(goal_hours - sched_hours, HOURS_PER_DAY)
sys.stdout.write('%-14s' % (d.strftime('%d %A') + ':'))
sys.stdout.write('* %.1f hours *' % hours_to_do)
sys.stdout.write('\n')
sched_hours += hours_to_do
d += timedelta(1)
border()
sys.stdout.write(
'at %.1f, goal %.1f, %.1f remain' % (total_hours, goal_hours,
goal_hours - total_hours))
sys.stdout.write('\n')
if __name__ == "__main__":
sys.exit(main(sys.argv))