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.
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'.
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.
'''
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
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:
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: <category>')
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]}: <category>')
+ print(f'Missing arg for pop {args[2]}: <category>')
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]}.')
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__":