from typing import (
Dict,
+ Iterable,
List,
+ Optional,
+ Tuple,
+ Union,
)
-TimeEntries = Dict[str,List[List[str]]]
+GroupNode = Tuple[str,List[Iterable['GroupNode']]]
+TimeEntries = Dict[str,List[Union[List[str],GroupNode]]]
TS_PATH='/home/users/.local/share/dwalde/time-sheet.csv'
'''
Print each punched in category, followed by the rest.
'''
- cats = list(entries.keys())
- cats.sort()
-
- outs = []
- print("In:")
- for cat in cats:
- if entries[cat][-1][0] == 'in':
- list_category(entries, cat)
- else:
- outs.append(cat)
-
- if len(cats) == len(outs):
- print("No categories punched in.")
-
- if len(outs) > 0:
- print("")
- print("Out:")
- print(f"({outs[0]}: {entries[outs[0]][0][1]})", end="")
- for i in range(1, len(outs)):
- cat = outs[i]
- print(f", ({outs[i]}: {entries[outs[i]][0][1]})", end="")
- print("")
+ stack = [(entries['tree'], -1)]
+ while len(stack) > 0:
+ item, depth = stack.pop()
+ group_id = item[0]
+ children = item[1]
+ if depth >= 0:
+ print("\t"*depth, end='')
+ text = ""
+ if group_id in entries:
+ name = entries[group_id][0][1]
+ last = entries[group_id][-1]
+ if last[0] == 'in':
+ text = f"\t punched in at {last[1]}"
+ print(group_id + ":\t" + name + " " + text)
+ else:
+ name = entries['groups'][group_id][0]
+ print(name)
+ for child in reversed(children):
+ stack.append((child, depth+1))
+
+
+def load_group_hierarchy(entries: TimeEntries):
+ groups = []
+ tree = ('', [])
+ id_to_node = {'' : tree}
+ entries['groups'] = {}
+ if '' in entries:
+ for entry in entries['']:
+ if len(entry) < 2:
+ continue
+ if entry[0] == 'group':
+ groups.append(entry)
+ entries['groups'][entry[1]] = entry[2:]
+ for cat in entries.keys():
+ if cat != '' and cat != 'groups':
+ groups.append([entries[cat][0][0]] + [cat] + entries[cat][0][1:])
+ last_process = 0
+ while len(groups) > 0 and last_process != len(groups):
+ skipped = []
+ for group in groups:
+ parent = ''
+ if len(group) > 3:
+ parent = group[3]
+ if parent in id_to_node:
+ id_to_node[parent][1].append((group[1], []))
+ id_to_node[group[1]] = id_to_node[parent][1][-1]
+ else:
+ skipped.append(group)
+ last_process = len(groups)
+ groups = skipped
+
+ for v in id_to_node.values():
+ v[1].sort()
+
+ entries['tree'] = tree
def load_file():
if row[0] not in entries:
entries[row[0]] = []
entries[row[0]].append(row[1:])
+ load_group_hierarchy(entries)
except FileNotFoundError:
pass
return entries
with open(TS_PATH, 'w', encoding='utf-8') as file:
writer = csv.writer(file)
for category, items in entries.items():
+ if category == 'tree' or category == 'groups':
+ continue
for item in items:
writer.writerow([category] + item)
-def new_category(entries: TimeEntries, category_id: str, category_name: str):
+def new_category(entries: TimeEntries, category_id: str, category_name: str, parent: Optional[str]):
'''
Create a new category with name and id/abbreviation.
'''
print(f'{category_id} already created: {entries[category_id][0]}')
exit(2)
- append_entry([category_id, 'category', category_name])
+ if parent is None:
+ parent = ''
+
+ append_entry([category_id, 'category', category_name, parent])
+
+
+def new_group(entries: TimeEntries, group_id: str,
+ group_name: str, parent: Optional[str]):
+ if 'groups' in entries:
+ for group in entries['groups']:
+ if group[1] == group_id:
+ print(f'{group_id} alread exists.')
+ exit(2)
+
+ if parent is None:
+ parent = ''
+
+ append_entry(['', 'group', group_id, group_name, parent])
def punch(entries: TimeEntries, dir: str, category: str, args: List[str] = []):
category = None
for cat, items in entries.items():
+ if cat == '' or cat == 'groups' or cat == 'tree':
+ continue
if items[-1][0] == 'in':
if category is not None:
print(f'At least two categories punched in ({category}, {cat}), please specify.')
total_time = datetime.timedelta(seconds=0)
for cat in categories:
+ if cat == '' or cat == 'groups' or cat == 'tree':
+ continue
td = datetime.timedelta(seconds=0)
punch_in = None
entries = load_file()
if args[1] == 'new':
- if len(args) < 4:
- print('Missing args for new category: <id> <name>')
- exit(1)
- new_category(entries, args[2], args[3])
+ if args[2] == 'group':
+ if len(args) < 5:
+ print('Missing args for new group: <id> <name> [<parent>]')
+ exit(1)
+ parent = None
+ if len(args) > 5:
+ parent = args[5]
+ new_group(entries, args[3], args[4], parent)
+ else:
+ if len(args) < 4:
+ print('Missing args for new category: <id> <name> [<parent>]')
+ exit(1)
+ parent = None
+ if len(args) > 4:
+ parent = args[4]
+ new_category(entries, args[2], args[3], parent)
elif args[1] == 'out':
if len(args) < 3: