Skip to content

Instantly share code, notes, and snippets.

@atronah
Created January 10, 2020 08:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save atronah/f161289163ca5d682dd289d438c1210e to your computer and use it in GitHub Desktop.
Save atronah/f161289163ca5d682dd289d438c1210e to your computer and use it in GitHub Desktop.
Eternity log parser
import csv
import datetime
import time
from itertools import groupby, chain
from functools import reduce
import click
def make_tree(data, group_indexes):
if group_indexes:
result = {}
group_column = group_indexes.pop(0)
key_func = lambda row: row[group_column]
for key, group in groupby(sorted(data, key=key_func), key=key_func):
result[key] = make_tree(group, list(group_indexes))
return result
return list(data)
def get_child_rows(node):
if isinstance(node, dict):
return chain.from_iterable(map(get_child_rows, node.values()))
else:
return node
def calculate_total_for_tree(node, column_index):
duration_values = (time.strptime(row[column_index], '%H:%M:%S') for row in get_child_rows(node))
return reduce(lambda x, y: x + datetime.timedelta(hours=y.tm_hour, minutes=y.tm_min, seconds=y.tm_sec),
duration_values,
datetime.timedelta())
def print_tree(tree, total_column_index, tags_column_index, indent=''):
if isinstance(tree, dict):
for key, node in tree.items():
total = calculate_total_for_tree(node, total_column_index)
if total.seconds == 0:
continue
tags_key_func = lambda row: row[tags_column_index]
tag_groups = groupby(sorted(get_child_rows(node), key=tags_key_func), key=tags_key_func)
totals = [('total', total)] + [(tag_name.strip(), calculate_total_for_tree(tag_rows, total_column_index))
for tag_name, tag_rows in tag_groups if tag_name.strip()]
print(f'{indent}- {key} ({", ".join([tag_name + ": " + str(tag_total) for tag_name, tag_total in totals])})')
print_tree(node, total_column_index, tags_column_index, indent + '\t')
@click.command()
@click.argument('filename')
@click.option('-c', '--columns',
default='date,start,end,duration,client,task,comment,tags',
help='specifies columns names (in accordance with their order in the file) for using in other params',
show_default=True)
@click.option('-g', '--grouping',
default='client,task,date,comment',
help='specifies column names for grouping and calculate total time',
show_default=True)
@click.option('-d', '--duration-column',
default='duration',
help='column name with duration time to calculate total',
show_default=True)
@click.option('-t', '--tags-column',
default='tags',
help='column name with tags to calculate totals by tags',
show_default=True)
def main(filename, columns, grouping, duration_column, tags_column):
columns = columns.split(',')
grouping = grouping.split(',')
with open(filename, newline='', encoding='utf-8') as cf:
cr = csv.reader(cf)
tree = make_tree((row for n, row in enumerate(cr) if n > 0), [columns.index(column_name) for column_name in grouping])
print_tree(tree, columns.index(duration_column), columns.index(tags_column))
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment