def tag(_tag, time="now") -> bool: time = XArrow.from_human(time) # time = human2arrow(time) if time > time.now(): raise BadTime(f"in the future: {time}") work = store.load() idx = -1 item = Entry(**work[idx]) if time < item.start: # Tag something in the past idx = -1 * next(i for i, work in enumerate(reversed(work), 1) if Entry(**work).start <= time) item_in_range = Entry(**work[idx]) if not confirm( f'{item.name_colored} started only at {c.time(item.start.strftime("%X"))}, ' f'Tag {item_in_range.name_colored} (started at {c.time(item_in_range.start.strftime("%X"))})?' ): return False item = item_in_range tag_colored = c.tag(_tag) if any( util.normalize_str(_tag) == t for t in map(util.normalize_str, item.tags)): print(f'{item.name_colored} already has tag {tag_colored}.') return False item.tags.add(_tag) work[idx]['tags'] = item.tags ok = store.dump(work) if ok: print(f"Okay, tagged {item.name_colored} with {tag_colored}.") else: print(f"Failed writing to sheet") return ok
def note(content, time="now"): time = XArrow.from_human(time) # time = human2arrow(time) if time > XArrow.now(): raise BadTime(f"in the future: {time}") content_and_time = content.strip() + f' ({time.HHmmss})' work = store.load() idx = -1 item = Entry(**work[idx]) if time < item.start: # Note for something in the past idx, item_in_range = next((i, item) for i, item in enumerate(map(lambda w: Entry(**w), reversed(work)), 1) if item.start.full == time.full) idx *= -1 if item_in_range.name == item.name: item = item_in_range else: if not confirm(f'{item.name_colored} started only at {c.time(item.start.strftime("%X"))},\n' f'note to {item_in_range.name_colored} (started at {c.time(item_in_range.start.strftime("%X"))})?'): return item = item_in_range for n in item.notes: if n.is_similar(content): if not confirm(f'{item.name_colored} already has this note: {c.b(c.note(n))}.\n' 'Add anyway?'): return item.notes.append(content_and_time) work[idx]['notes'] = item.notes store.dump(work) print(f'Noted {c.b(c.note(content_and_time))} to {item.name_colored}')
def test_from_human_static(self): now = XArrow.from_human('now') today = XArrow.from_human('today') assert now == today == XArrow.now() yesterday = XArrow.from_human('yesterday') assert yesterday == now.shift(days=-1) assert yesterday.day == now.day - 1 eight_days_ago = XArrow.from_human('8 days ago') assert eight_days_ago == now.shift(days=-8) assert eight_days_ago.day == now.day - 8 dec_first_21 = XArrow.from_human('01/12/21') assert dec_first_21.year == 2021 assert dec_first_21.month == 12 assert dec_first_21.day == 1 thursday = XArrow.from_human('thursday') assert thursday.strftime('%A') == 'Thursday'
def parse_args(argv=[]) -> tuple[Callable, dict]: if not argv: argv = sys.argv # *** log # ** timefred # timefred -> log(detailed=True) argv_len = len(argv) if argv_len == 1: return action.log, {'detailed': True} # ** timefred thursday if len(argv[1]) > 1: if argv[1].lower() == 'yesterday': return action.log, {'time': argv[1], 'detailed': True} with suppress(ValueError): isoweekday(argv[1]) return action.log, {'time': argv[1], 'detailed': True} head = argv[1] tail: list[str] = argv[2:] # ** log if head in ('l', 'l-', 'log', 'log-'): groupby = None with suppress(ValueError, AttributeError): groupby_idx = tail.index('-g') groupby = tail[groupby_idx + 1] tail = tail[:groupby_idx] if tail: time = ' '.join(tail) else: time = 'today' args = {'time': time, 'detailed': '-' not in head, 'groupby': groupby} return action.log, args # *** help if 'help' in head or head in ('-h', 'h'): print(__doc__, file=sys.stderr) sys.exit(0) # *** edit elif head in ('e', 'edit'): return action.edit, {} # *** on elif head in ('+', 'o', 'on'): if not tail: raise BadArguments("Need the name of whatever you are working on.") name = tail.pop(0) _tag = None _note = None if tail: with suppress(ValueError): _tag_idx = tail.index('-t') _tag = tail[_tag_idx + 1] tail = tail[:_tag_idx] with suppress(ValueError): _note_idx = tail.index('-n') _note = tail[_note_idx + 1] tail = tail[:_note_idx] time = XArrow.from_human(' '.join(tail) if tail else 'now') else: time = XArrow.now() args = {'name': name, 'time': time, 'tag': _tag, 'note': _note} return action.on, args # *** stop elif head in ('-', 'stop'): args = {'time': XArrow.from_human(' '.join(tail) if tail else 'now')} return action.stop, args # *** status elif head in ('?', '??', 's', 's+', 'status', 'status+'): args = {'show_notes': '+' in head} return action.status, args # *** tag elif head in ('t', 'tag'): if not tail: raise BadArguments("Please provide a tag.") if len(tail) == 2: _tag, time = tail args = {'tag': _tag, 'time': time} elif len(tail) == 1: args = {'tag': tail[0]} else: args = {'tag': ' '.join(tail)} return action.tag, args # *** note elif head in ('n', 'note'): if not tail: raise BadArguments("Please provide some text to be noted.") if len(tail) == 2: content, time = tail args = {'content': content, 'time': time} elif len(tail) == 1: args = {'content': tail[0]} else: args = {'content': ' '.join(tail)} return action.note, args # *** interrupt # elif head in ('i', 'interrupt'): # if not tail: # raise BadArguments("Need the name of whatever you are working on.") # # name = tail.pop(0) # args = { # 'name': name, # 'time': human2formatted(' '.join(tail) if tail else 'now'), # } # return interrupt, args # *** aggregate elif head == 'a' or head.startswith('ag'): if not tail: raise BadArguments("Need at least <start> <stop>") if len(tail) == 1: times = tail[0] if '-' in times: start, stop = map(str.strip, times.partition('-')) elif ' ' in times: start, stop = map(str.strip, times.partition(' ')) else: raise BadArguments("Need at least <start> <stop>") else: start, stop, *tail = tail # start_arw = human2arrow(start) # stop_arw = human2arrow(stop) start_arw = XArrow.from_human(start) stop_arw = XArrow.from_human(stop) breakpoint() # *** _dev if head == '_dev': if tail[0] == 'generate completion': from timefred._dev import generate_completion return generate_completion, {} raise BadArguments("I don't understand %r" % (head, ))