From: Dustin Walde Date: Fri, 20 Jan 2023 05:40:37 +0000 (-0800) Subject: Add improved listing and querying X-Git-Url: https://git.walde.dev/?a=commitdiff_plain;h=2904f4c104ed009da751408eddb41f87ba1bacf1;p=punch Add improved listing and querying - Query time totals since date - List current state of categories - Other minor improvements --- diff --git a/tt.py b/tt.py index 362bcde..741269d 100755 --- a/tt.py +++ b/tt.py @@ -65,6 +65,34 @@ def list_entries(entries: TimeEntries): list_category(entries, cat) +def list_status(entries: TimeEntries): + ''' + 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(len(outs)): + cat = outs[i] + print(f", ({outs[i]}: {entries[outs[i]][0][1]})", end="") + print("") + + def load_file(): ''' Loads the current state of the time sheet into memory. @@ -105,7 +133,7 @@ def new_category(entries: TimeEntries, category_id: str, category_name: str): append_entry([category_id, 'category', category_name]) -def punch(entries: TimeEntries, dir: str, category: str): +def punch(entries: TimeEntries, dir: str, category: str, args: List[str] = []): ''' Attempt to add and entry to punch a category in or out. _dir_ is the string 'in' or 'out'. @@ -124,14 +152,21 @@ def punch(entries: TimeEntries, dir: str, category: str): exit(3) now = datetime.datetime.now() - now = datetime.datetime.now(now.astimezone().tzinfo) - entry = [category, dir, now.isoformat()] + if len(args) < 2 or args[0] != "at": + time = datetime.datetime.now(now.astimezone().tzinfo) + else: + time = datetime.datetime.combine(datetime.date.today(), + datetime.time( + hour=int(args[1].split(':')[0]), + minute=int(args[1].split(':')[1]), + tzinfo=now.astimezone().tzinfo)) + entry = [category, dir, time.isoformat()] append_entry(entry) entries[category].append(entry[1:]) list_category(entries, category) -def try_punch_out(entries: TimeEntries): +def try_punch_out(entries: TimeEntries) -> bool: ''' Checks if exactly one category is currently punched in and punches it out. ''' @@ -146,9 +181,54 @@ def try_punch_out(entries: TimeEntries): if category is None: print('No categories punched in.') - exit(2) + return False punch(entries, 'out', category) + return True + + +# Query options + +def query_data(entries: TimeEntries, args: List[str]): + categories = [] + i = 0 + arg = None + start = None + + while i < len(args): + if arg == 'total': + if args[i] == 'all': + categories.extend(entries.keys()) + else: + categories.append(args[i]) + arg = None + + elif arg == 'from': + start = datetime.datetime.fromisoformat(args[i]).astimezone() + arg = None + + else: + if args[i] in { 'total', 'from' }: + arg = args[i] + + i += 1 + + for cat in categories: + td = datetime.timedelta(seconds=0) + punch_in = None + + for entry in entries[cat]: + if punch_in is None and entry[0] == 'in': + time = datetime.datetime.fromisoformat(entry[1]) + if start is None or time >= start: + punch_in = time + + elif punch_in is not None and entry[0] == 'out': + time = datetime.datetime.fromisoformat(entry[1]) + td += time - punch_in + punch_in = None + + print(f"{cat}:\t{td}") # Service specific @@ -172,7 +252,7 @@ def run_service(): if now - last_notify[cat] > datetime.timedelta(minutes=30): clock = now - punch hours = int(clock.seconds/60/60) - minutes = int(clock.seconds/60) + minutes = int(clock.seconds/60%60) if hours > 0: timestr = f'{hours}h {minutes}m' else: @@ -208,32 +288,29 @@ def main(args): if len(args) < 3: try_punch_out(entries) else: - punch(entries, 'out', args[2]) + punch(entries, 'out', args[2], args[3:]) elif args[1] == 'in': if len(args) < 3: print(f'Missing arg for punch in: ') exit(1) - punch(entries, 'in', args[2]) + punch(entries, 'in', args[2], args[3:]) elif args[1] == 'pop': if len(args) < 3: - print(f'Missing arg for pop {args[1]}: ') + print(f'Missing arg for pop {args[2]}: ') exit(1) if args[2] not in entries: print(f'Category does not exist {args[2]}.') exit(2) last_entry = entries[args[2]][-1] - resp = input(f"Remove entry {last_entry} [y/N]?") + resp = input(f"Remove entry {last_entry} [y/N]? ") if resp.lower() == 'y': entries[args[2]].pop() save_file(entries) list_category(entries, args[2]) elif args[1] == 'print': - print(entries) - - elif args[1] == 'list': if len(args) > 2: if args[2] not in entries: print(f'Category does not exist {args[2]}.') @@ -242,8 +319,18 @@ def main(args): else: list_entries(entries) + elif args[1] == 'to': + if len(args) < 3: + print(f'Missing category to punch in to.') + exit(1) + try_punch_out(entries) + punch(entries, 'in', args[2]) + + elif args[1] == 'list': + list_status(entries) + elif args[1] == 'query': - pass + query_data(entries, args[2:]) if __name__ == "__main__":