Created
October 11, 2019 08:37
-
-
Save KennethNielsen/175923fb868ae02ad8cc2311626a3515 to your computer and use it in GitHub Desktop.
Quick and dirty script for calculating hours balance from Watson output
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
import subprocess | |
import sys | |
import arrow | |
import argparse | |
from json import loads | |
from pprint import pprint | |
from collections import Counter, defaultdict | |
parser = argparse.ArgumentParser(description='Show the hour balance.') | |
parser.add_argument( | |
'offset', | |
type=str, | |
nargs='?', | |
default=None, | |
help=( | |
'Apply a month offset. If given, the start and end dates will be the first and last ' | |
'day of the offset month' | |
), | |
) | |
parser.add_argument( | |
'--from', type=str, default=None, dest="fromdate", | |
help=( | |
'The arrow parseable date/datetime to count from. ' | |
'--from will override that start date set by default behavior or with offset' | |
) | |
) | |
parser.add_argument( | |
'--to', | |
type=str, | |
default=None, | |
dest="todate", | |
help=( | |
'The arrow parseable date/datetime to count to. ' | |
'--to will override that start date set by default behavior or with offset' | |
), | |
) | |
args = parser.parse_args() | |
# Calculate from and to, possibly applying an offset | |
if args.offset: | |
now = arrow.now().shift(months=int(args.offset)) | |
from_ = now.floor('month') | |
to_ = now.ceil("month") | |
else: | |
now = arrow.now() | |
from_ = now.floor('month') | |
to_ = now.shift(days=-1) | |
if args.fromdate: | |
from_ = arrow.get(args.fromdate) | |
if args.todate: | |
to_ = arrow.get(args.todate) | |
log_json = subprocess.check_output( | |
f"watson log --json --from \"{from_.format('YYYY-MM-DD HH:mm:ss')}\" --to \"{to_.format('YYYY-MM-DD HH:mm:ss')}\"", | |
shell=True, | |
) | |
log = loads(log_json) | |
# Find all the workdays in the range | |
work_days = [] | |
for d in arrow.Arrow.range("day", from_, to_): | |
if d.isoweekday() in range(1, 6): | |
work_days.append(d.date()) | |
# Collect hours and activities | |
days = Counter() | |
activities = defaultdict(lambda: defaultdict(set)) | |
for item in log: | |
start = arrow.get(item["start"]) | |
stop = arrow.get(item["stop"]) | |
diff = stop - start | |
days[start.date()] += diff.seconds | |
activities[start.date()][item["project"]].update(set(item["tags"])) | |
### Form the strings for the status overview | |
outstrings = [] | |
baddates = [] | |
for d in arrow.Arrow.range("day", from_, to_): | |
# Put line markers after Friday and Sunday | |
daynum = d.isoweekday() | |
if daynum == 1 and d.day != 1: | |
outstrings.append("=") | |
elif daynum == 6 and d.day != 1: | |
outstrings.append("-") | |
dayname = arrow.locales.EnglishLocale.day_names[daynum] | |
daysum = days.get(d.date(), None) | |
if daysum: | |
daysumstring = format(daysum / 3600, " >5.1f") + " hours Act:" | |
for project, tags in activities.get(d.date(), {}).items(): | |
daysumstring += f" {project}({', '.join(tags)})" | |
else: | |
if d.isoweekday() in range(1, 6): | |
daysumstring = "!!!!!!! no hours on a weekday, is this really accurate" | |
baddates.append(format(d.date())) | |
else: | |
daysumstring = "" | |
outstrings.append("{} {:>9} {}".format(d.date(), dayname, daysumstring)) | |
length = max(len(s) for s in outstrings) + 1 | |
for s in outstrings: | |
if s in ("=", "-"): | |
print(s * length) | |
else: | |
print(s) | |
#print(daynum, dayname) | |
expected = len(work_days) * 7.9 | |
got = sum(days.values()) / 3600 | |
print("\n\n") | |
print("### TOTALS " + "#" * (length - 11)) | |
print() | |
print(f"Period: {from_.date()} -> {to_.date()}") | |
if baddates: | |
print() | |
print("The following weekdays without hours were detected in the range") | |
print(", ".join(baddates)) | |
print() | |
filler = " " * len(str(len(work_days))) | |
print("Expected ({} days): {: >6.1f}".format(len(work_days), expected)) | |
print("Got {} : {: >6.1f}".format(filler, got)) | |
balance_word = "behind" if expected > got else "ahead" | |
print("Diff {} : {: >6.1f} {}".format(filler, abs(expected - got), balance_word)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment