Skip to content

Instantly share code, notes, and snippets.

@KennethNielsen
Created October 11, 2019 08:37
Show Gist options
  • Save KennethNielsen/175923fb868ae02ad8cc2311626a3515 to your computer and use it in GitHub Desktop.
Save KennethNielsen/175923fb868ae02ad8cc2311626a3515 to your computer and use it in GitHub Desktop.
Quick and dirty script for calculating hours balance from Watson output
#!/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