def test_from_absolute_DDMMYYHHmmss(self): datetime = "13/12/21 11:23:45" from_absolute = XArrow.from_absolute(datetime) assert from_absolute.day == 13 assert from_absolute.month == 12 assert from_absolute.year == 2021 assert from_absolute.hour == 11 assert from_absolute.minute == 23 assert from_absolute.second == 45 datetime = "13/12 11:23:45" from_absolute = XArrow.from_absolute(datetime) assert from_absolute.day == 13 assert from_absolute.month == 12 assert from_absolute.hour == 11 assert from_absolute.minute == 23 assert from_absolute.second == 45 datetime = "13/12/21 11:23" from_absolute = XArrow.from_absolute(datetime) assert from_absolute.day == 13 assert from_absolute.month == 12 assert from_absolute.year == 2021 assert from_absolute.hour == 11 assert from_absolute.minute == 23 assert from_absolute.second == 0 datetime = "13/12 11:23" from_absolute = XArrow.from_absolute(datetime) assert from_absolute.day == 13 assert from_absolute.month == 12 assert from_absolute.hour == 11 assert from_absolute.minute == 23 assert from_absolute.second == 00
def test_from_formatted(self): test_start_from_formatted = XArrow.from_formatted(TEST_START_ARROW) assert_arrows_soft_eq(test_start_from_formatted, TEST_START_ARROW) assert test_start_from_formatted is TEST_START_ARROW for fmt, expected_attrs in { FORMATS.date: ['year', 'month', 'day'], FORMATS.short_date: ['month', 'day'], FORMATS.time: ['hour', 'minute', 'second'], FORMATS.short_time: ['hour', 'minute'], FORMATS.datetime: ['year', 'month', 'day', 'hour', 'minute', 'second'], FORMATS.shorter_datetime: ['year', 'month', 'day', 'hour', 'minute'], FORMATS.short_datetime: ['month', 'day', 'hour', 'minute'], }.items(): formatted = TEST_START_ARROW.format(fmt) from_formatted: XArrow = XArrow.from_formatted(formatted) assert_equal_attrs(from_formatted, TEST_START_ARROW, *expected_attrs) for not_included_attr in TIME_UNITS - set(expected_attrs): assert getattr( from_formatted, not_included_attr) == 0 if not_included_attr in { 'hour', 'minute', 'second' } else 1
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_arrows2rel_time(): present = XArrow.now() past = present.shift(weeks=-3, days=-3) assert arrows2rel_time(present, past) == '3 weeks & 3 days ago' present = XArrow.from_absolute('21/12/21') past = XArrow.from_absolute('01/12/21') secs = int((present - past).total_seconds()) assert 20 * 24 * 3600 <= secs <= 21 * 24 * 3600 ret = arrows2rel_time(present, past) assert ret == '2 weeks & 6 days ago'
def test_from_absolute_HHmmss(self): HHmmss = "02:00:00" from_absolute = XArrow.from_absolute(HHmmss) assert from_absolute.hour == 2 assert from_absolute.minute == 0 assert from_absolute.second == 0 HHmmss = "02:53:49" from_absolute = XArrow.from_absolute(HHmmss) assert from_absolute.hour == 2 assert from_absolute.minute == 53 assert from_absolute.second == 49
def test_dehumanize_static(self): now_dehumanized = XArrow.dehumanize("now") now = XArrow.now() assert_arrows_soft_eq(now_dehumanized, now) today = XArrow.dehumanize('today') assert_arrows_soft_eq(today, now) yesterday = XArrow.dehumanize('yesterday') now_shift_yesterday = now.shift(days=-1) assert_arrows_soft_eq(now_shift_yesterday, yesterday) tomorrow = XArrow.dehumanize('tomorrow') now_shift_tomorrow = now.shift(days=+1) assert_arrows_soft_eq(now_shift_tomorrow, tomorrow)
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 on(self, name: Union[str, Activity], time: Union[str, XArrow] = None, tag: Union[str, Tag] = None, note: Union[str, Note] = None) -> Activity: """ Raises: ValueError: if an activity with the same / similar name is already ongoing """ if time is None: time = XArrow.now() try: ongoing_activity = self.ongoing_activity() except ValueError: # No ongoing activity -> start new activity day = self[time.DDMMYY] activity: Activity = day[name] activity.start(time, tag, note) return activity else: # Ongoing activity -> stop it and start new activity if name == ongoing_activity.name: raise ValueError( f'{ongoing_activity.shortrepr()} is already ongoing') if ongoing_activity.has_similar_name(name): raise ValueError( f'{ongoing_activity.shortrepr()} is ongoing, and has a similar name to {name!r}' ) ongoing_activity.stop(time) day = self[time.DDMMYY] activity: Activity = day[name] activity.start(time, tag, note) return activity
def stop(self, time: Union[str, XArrow] = None, tag: Union[str, Tag] = None, note: Union[str, Note] = None) -> Entry: """ Returns: Last entry. Raises: ValueError: if the activity is not ongoing """ last_entry = self.safe_last_entry() if not last_entry or last_entry.end: raise ValueError(f'{self.shortrepr()} is not ongoing') if not time: time = XArrow.now() if last_entry.start > time: raise ValueError( f'Cannot stop {self.shortrepr()} before start time (tried to stop at {time!r})' ) last_entry.end = time if tag: last_entry.tags.add(tag) if note: last_entry.notes.append(note) return last_entry
def test_on_got_to_office_08_20(self): work = Work() '''{ "02/11/21" : Day }''' assert isinstance(work, Work) assert isinstance(work, dict) assert not work assert len(work) == 0 entry = Entry(start="02:20") assert entry assert entry.start assert isinstance(entry.start, XArrow) absolute = XArrow.from_absolute("02:20") entry_start = entry.start assert_arrows_soft_eq(entry_start, absolute) name = "Got to office" activity = Activity(name=name) assert not activity # because list assert len(activity) == 0 assert isinstance(activity.name, Colored) assert activity.name == name activity.append(entry) assert activity assert len(activity) == 1 assert activity[0] == entry day = work[entry.start.DDMMYY] assert isinstance(day, Day) day[name] = activity assert day[ name] == activity, f"{day[str(activity.name)] = }, {activity = }" assert work assert len(work) == 1
def test_from_absolute_now(self): now = XArrow.now().replace(second=0) from_absolute_now = XArrow.from_absolute(now) assert_arrows_soft_eq(from_absolute_now, now) assert from_absolute_now is now for fmt in [ # FORMATS.date, # FORMATS.short_date: ['month', 'day'], FORMATS.time, FORMATS.short_time, # FORMATS.datetime: ['year', 'month', 'day', 'hour', 'minute', 'second'], # FORMATS.shorter_datetime: ['year', 'month', 'day', 'hour', 'minute'], # FORMATS.short_datetime: ['month', 'day', 'hour', 'minute'], ]: formatted = now.format(fmt) from_absolute_formatted: XArrow = XArrow.from_absolute( formatted) assert_arrows_soft_eq(from_absolute_formatted, now)
def test_from_day(self): days = [ 'sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday' ] for num, day in enumerate(days, start=1): for char_index in range(2, len(day) + 1): arrow_from_day = XArrow.from_day(day[:char_index]) assert arrow_from_day.strftime('%A').lower() == day
def test_log_01_12_21(capsys): raw_data = dedent(''' ["01/12/21"] "Got to office" = "10:00" [["01/12/21"."Integration"]] synced = true start = 10:30:00 end = 11:00:00 [["01/12/21"."Integration"]] synced = true start = 11:20:00 end = 11:40:00 [["01/12/21"."Integration - Weak WIFI Password"]] synced = true start = 13:30:00 end = 13:40:00 notes = {"13:40:00" = "With Vlad, tested, done, everything as expected"} [["01/12/21"."immediate endTime quiet time default value is empty str"]] start = 14:00:00 end = 17:30:00 jira = "ASM-13925" synced = true ''') sheet_path = '/tmp/timefred-sheet--test-action--test-log--test-log-01-12-21.toml' with open(sheet_path, 'w') as sheet: sheet.write(raw_data) with temp_sheet(sheet_path): log_action('01/12/21') captured = capsys.readouterr() output = captured.out output_lines = output.splitlines() now = XArrow.now() past = XArrow.from_absolute('01/12/21') from timefred.time.timeutils import arrows2rel_time assert decolor(output_lines[0] ) == f'Wednesday, 01/12/21 | {arrows2rel_time(now, past)}'
def test_sub(self): minute = 60 hour = minute * 60 day = hour * 24 week = day * 7 present = XArrow.now() past = present.shift(weeks=-3, days=-3) td = present - past assert td.days == 3 * 7 + 3 secs = int(td.total_seconds()) assert secs == 3 * week + 3 * day
def test_init_with_all_fields(self): entry = Entry(start="02:00:00", end="03:00:00", synced=True, notes=[{ "02:30:00": "PR-5000" }], tags=["meeting"]) assert entry assert entry.start == XArrow.from_absolute("02:00:00") assert entry.end == XArrow.from_absolute("03:00:00") assert entry.synced is True assert entry.notes assert isinstance(entry.notes, list) assert entry.notes == [Note({"02:30:00": "PR-5000"})] assert isinstance(entry.notes[0], Note) assert entry.notes[0] == Note({"02:30:00": "PR-5000"}) assert entry.notes[0].time == XArrow.from_absolute("02:30:00") assert entry.notes[0].content == "PR-5000" assert entry.tags == [Tag("meeting")] assert next(iter(entry.tags)) == Tag("meeting") assert isinstance(next(iter(entry.tags)), Tag)
def test_sanity(self): note = Note({"17:00": "PR-6091"}) assert isinstance(note.time, XArrow) assert note.time == XArrow.from_absolute("17:00") assert note.time.hour == 17 assert note.time.minute == 0 assert note.time.second == 0, note.time.second assert note.content == "PR-6091" if not os.getenv('TIMEFRED_REPR', '').lower() in ('no', 'disable'): assert repr( note ) == f"Note(content = 'PR-6091', time = XArrow ⟨{TEST_START_ARROW.DDMMYY} 17:00:00⟩)", repr( note)
def stop(self, time: Union[str, XArrow] = None, tag: Union[str, Tag] = None, note: Union[str, Note] = None) -> Optional[Activity]: """ Raises: ValueError: if there is no ongoing activity """ ongoing_activity = self.ongoing_activity() if not time: time = XArrow.now() ongoing_activity.stop(time=time, tag=tag, note=note) stopped_activity = ongoing_activity return stopped_activity
def default_work(day: XArrow = None) -> Work: """ Returns Work of one day with a single activity, "Got to office": [{"start": "02:20"}] """ if not day: day = XArrow.now() sheet = { day.DDMMYY: { "Got to office": [{ "start": "02:20:00" }], # "Integration": [{"start": "02:20:00", "end": "02:30:00"}] } } work = Work(Day, **sheet) return work
def test_stop_when_not_ongoing(self): log.title(f"test_models.test_stop_when_not_ongoing()") work = default_work(TEST_START_ARROW) day: Day = work.__getitem__(TEST_START_ARROW.DDMMYY) got_to_office_activity: Activity = day.__getitem__("Got to office") assert got_to_office_activity.ongoing() is True now = XArrow.now() entry: Entry = got_to_office_activity.stop(now) assert entry is got_to_office_activity.safe_last_entry() assert entry.end == now assert got_to_office_activity.ongoing() is False with assert_raises(ValueError, f'{got_to_office_activity} is not ongoing'): got_to_office_activity.stop()
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 status(show_notes=False): ensure_working() data = store.load() current = Entry(**data[-1]) duration = Timespan(current.start, XArrow.now()).human_duration # diff = timegap(current.start, now()) # notes = current.get('notes') if not show_notes or not current.notes: print( f'You have been working on {current.name.colored} for {c.time(duration)}.' ) return print('\n '.join([ f'You have been working on {current.name_colored} for {c.time(duration)}.\nNotes:', # [rgb(170,170,170)] *[f'{c.grey100("o")} {n.pretty()}' for n in current.notes] ]))
def test_stop_before_last_entry_started(self): log.title(f"test_models.test_stop_before_last_entry_started()") work = default_work(TEST_START_ARROW) day: Day = work.__getitem__(TEST_START_ARROW.DDMMYY) assert work[TEST_START_ARROW.DDMMYY] is day got_to_office_activity: Activity = day.__getitem__("Got to office") assert got_to_office_activity.ongoing() is True assert day['Got to office'] is got_to_office_activity last_entry: Entry = got_to_office_activity[-1] assert got_to_office_activity.safe_last_entry() is last_entry yesterday = XArrow.dehumanize('yesterday') with assert_raises( ValueError, f'Cannot stop {got_to_office_activity.shortrepr()} before start time (tried to stop at {yesterday!r})' ): got_to_office_activity.stop(yesterday)
def test_from_absolute_time(self): from datetime import time abs_time = time(hour=11, minute=0, second=0) from_absolute = XArrow.from_absolute(abs_time) assert from_absolute.hour == 11 assert from_absolute.minute == 0 assert from_absolute.second == 0 abs_time = time(hour=11, minute=23, second=0) from_absolute = XArrow.from_absolute(abs_time) assert from_absolute.hour == 11 assert from_absolute.minute == 23 assert from_absolute.second == 0 abs_time = time(hour=11, minute=23, second=45) from_absolute = XArrow.from_absolute(abs_time) assert from_absolute.hour == 11 assert from_absolute.minute == 23 assert from_absolute.second == 45 abs_time = time(hour=11, minute=0, second=45) from_absolute = XArrow.from_absolute(abs_time) assert from_absolute.hour == 11 assert from_absolute.minute == 0 assert from_absolute.second == 45 abs_time = time(hour=0, minute=0, second=45) from_absolute = XArrow.from_absolute(abs_time) assert from_absolute.hour == 0 assert from_absolute.minute == 0 assert from_absolute.second == 45 abs_time = time(hour=0, minute=23, second=45) from_absolute = XArrow.from_absolute(abs_time) assert from_absolute.hour == 0 assert from_absolute.minute == 23 assert from_absolute.second == 45
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, ))
def test_from_absolute_DDMMYY(self): DDMMYY = "13/12/21" from_absolute = XArrow.from_absolute(DDMMYY) assert from_absolute.day == 13 assert from_absolute.month == 12 assert from_absolute.year == 2021
def test_from_absolute_HHmm(self): HHmm = "02:00" from_absolute = XArrow.from_absolute(HHmm) assert from_absolute.hour == 2 assert from_absolute.minute == 0 assert from_absolute.second == 0
def test_dehumanize_advanced( self): # can decide not to support if too difficult XArrow.dehumanize('1 days from now') XArrow.dehumanize('1 days from today')
def test_dehumanize_instance(self): now = XArrow.now() now_dehumanized = now.dehumanize("now") assert_arrows_soft_eq(now_dehumanized, now) today = now.dehumanize('today') assert_arrows_soft_eq(today, now) assert_arrows_soft_eq(today, now_dehumanized) # * Past # 1 day ago yesterday = now.dehumanize('yesterday') assert_arrows_soft_eq(now.shift(days=-1), yesterday) a_day_ago = now.dehumanize('a day ago') assert_arrows_soft_eq(a_day_ago, yesterday) _1_day_ago = now.dehumanize('1 day ago') assert_arrows_soft_eq(_1_day_ago, yesterday) _1d_ago = now.dehumanize('1d ago') assert_arrows_soft_eq(_1d_ago, yesterday) _1_days_ago = now.dehumanize('1 days ago') assert_arrows_soft_eq(_1_days_ago, yesterday) _1_d_ago = now.dehumanize('1 d ago') assert_arrows_soft_eq(_1_d_ago, yesterday) a_day = now.dehumanize('a day') assert_arrows_soft_eq(a_day, yesterday) _1_day = now.dehumanize('1 day') assert_arrows_soft_eq(_1_day, yesterday) _1d = now.dehumanize('1d') assert_arrows_soft_eq(_1d, yesterday) _1_d = now.dehumanize('1 d') assert_arrows_soft_eq(_1_d, yesterday) _1_days = now.dehumanize('1 days') assert_arrows_soft_eq(_1_days, yesterday) # 5 days ago five_days_ago = now.shift(days=-5) _5_day_ago = now.dehumanize('5 day ago') assert_arrows_soft_eq(_5_day_ago, five_days_ago) _5d_ago = now.dehumanize('5d ago') assert_arrows_soft_eq(_5d_ago, five_days_ago) _5_days_ago = now.dehumanize('5 days ago') assert_arrows_soft_eq(_5_days_ago, five_days_ago) _5_d_ago = now.dehumanize('5 d ago') assert_arrows_soft_eq(_5_d_ago, five_days_ago) _5_day = now.dehumanize('5 day') assert_arrows_soft_eq(_5_day, five_days_ago) _5d = now.dehumanize('5d') assert_arrows_soft_eq(_5d, five_days_ago) _5_d = now.dehumanize('5 d') assert_arrows_soft_eq(_5_d, five_days_ago) _5_days = now.dehumanize('5 days') assert_arrows_soft_eq(_5_days, five_days_ago) # months _5_months = now.dehumanize('5 months') assert_arrows_soft_eq(_5_months, now.shift(months=-5)) _5_month = now.dehumanize('5 month') assert_arrows_soft_eq(_5_month, now.shift(months=-5)) # Complex _5h_32m_1s_ago = now.dehumanize('5h 32m 1s ago') assert_arrows_soft_eq(_5h_32m_1s_ago, now.shift(hours=-5, minutes=-32, seconds=-1)) _5_minutes_5_months = now.dehumanize('5 minutes 5 months') assert_arrows_soft_eq(_5_minutes_5_months, now.shift(minutes=-5, months=-5)) _5m_5_months = now.dehumanize('5m 5 months') assert_arrows_soft_eq(_5m_5_months, now.shift(minutes=-5, months=-5)) _5m_5M = now.dehumanize('5m 5M') assert_arrows_soft_eq(_5m_5M, now.shift(minutes=-5, months=-5)) _5m_5M_7w = now.dehumanize('5m 5M 7w') now_shift_5_minutes_5_months_7_weeks_ago = now.shift(minutes=-5, months=-5, weeks=-7) assert_arrows_soft_eq(_5m_5M_7w, now_shift_5_minutes_5_months_7_weeks_ago) for perm in permutations(['5m', '5M', '7w'], 3): complex = now.dehumanize(' '.join(perm)) assert_arrows_soft_eq( complex, now_shift_5_minutes_5_months_7_weeks_ago) # * Future in_1_days = now.dehumanize('in 1 days') tomorrow = now.dehumanize('tomorrow') assert_arrows_soft_eq(in_1_days, tomorrow) assert_arrows_soft_eq(now.shift(days=+1), tomorrow) assert_arrows_soft_eq(now.shift(days=+1), in_1_days) in_1_day = now.dehumanize('in 1 day') assert_arrows_soft_eq(in_1_day, tomorrow) in_5_days = now.dehumanize('in 5 days') assert_arrows_soft_eq(now.shift(days=+5), in_5_days) in_5_day = now.dehumanize('in 5 day') assert_arrows_soft_eq(in_5_day, now.shift(days=+5)) now_shift_in_5_minutes_5_months_7_weeks = now.shift(minutes=+5, months=+5, weeks=+7) for perm in permutations(['5m', '5M', '7w'], 3): complex = now.dehumanize('in ' + ' '.join(perm)) assert_arrows_soft_eq(complex, now_shift_in_5_minutes_5_months_7_weeks)
from timefred.time import XArrow print() import os os.environ.setdefault('TIMEFRED_TESTING', '1') print(f"{os.environ['TIMEFRED_TESTING'] = }") TEST_START_ARROW: XArrow = XArrow.now()
def test_dehumanize_vanilla(self): """Make sure we don't break vanilla Arrow.dehumanize() functionality""" log.title('TestXArrow.Test_dehumanize.test_dehumanize_vanilla') now = XArrow.now() now_dehumanized = XArrow.dehumanize("now") assert_arrows_soft_eq(now_dehumanized, now) assert_arrows_soft_eq(XArrow.dehumanize("just now"), now) # * 1 unit # "hour": "an hour", "days": "{0} days" for unit, expression in EnglishLocale.timeframes.items(): for relative_fmt in ("{0} ago", "in {0}"): if 'now' in expression: continue if '{0}' in expression: shift = randint(2, 4) hardcoded_number = expression.format( shift) # 3 seconds human_expression = relative_fmt.format( hardcoded_number) # 3 seconds ago / in 3 seconds else: shift = 1 human_expression = relative_fmt.format( expression) # a second ago / in a second dehumanized_static = XArrow.dehumanize(human_expression) if 'ago' in relative_fmt: shift *= -1 shift_kwargs = {unit.removesuffix('s') + 's': shift} now = XArrow.now() now_shifted = now.shift(**shift_kwargs) try: assert_arrows_soft_eq(dehumanized_static, now_shifted) except AssertionError: dehumanized_static = XArrow.dehumanize( human_expression) now = XArrow.now() now_shifted = now.shift(**shift_kwargs) assert_arrows_soft_eq(dehumanized_static, now_shifted) dehumanized_instance = now.dehumanize(human_expression) assert_arrows_soft_eq(dehumanized_instance, now_shifted) # * 2 units for time_unit_1 in TIME_UNITS: for time_unit_2 in TIME_UNITS: if time_unit_1 == time_unit_2: continue if random() < 0.5: continue shift_1 = randint(2, 4) shift_2 = randint(2, 4) singular_time_unit_1 = (f"a" if time_unit_1 != "hour" else "an") + f" {time_unit_1}" singular_time_unit_2 = (f"a" if time_unit_2 != "hour" else "an") + f" {time_unit_2}" plural_time_unit_1 = f"{shift_1} {time_unit_1}s" plural_time_unit_2 = f"{shift_2} {time_unit_2}s" expressions = {} for fmt in ["{0} and {1}", "{0}, {1}", "{0} {1}"]: expressions[ fmt.format(plural_time_unit_1, plural_time_unit_2) + " ago"] = (True, True) expressions["in " + fmt.format( plural_time_unit_1, plural_time_unit_2)] = (True, True) expressions[fmt.format(plural_time_unit_1, singular_time_unit_2) + " ago"] = (True, False) expressions["in " + fmt.format(plural_time_unit_1, singular_time_unit_2)] = (True, False) expressions[fmt.format(singular_time_unit_1, plural_time_unit_2) + " ago"] = (False, True) expressions["in " + fmt.format(singular_time_unit_1, plural_time_unit_2)] = (False, True) expressions[fmt.format(singular_time_unit_1, singular_time_unit_2) + " ago"] = (False, False) expressions["in " + fmt.format(singular_time_unit_1, singular_time_unit_2)] = (False, False) for human_expression, quantity_tuple in expressions.items( ): shift_kwargs = {} sign = 1 if human_expression.startswith("in ") else -1 if quantity_tuple[0]: shift_kwargs[time_unit_1 + 's'] = shift_1 * sign else: shift_kwargs[time_unit_1 + 's'] = 1 * sign if quantity_tuple[1]: shift_kwargs[time_unit_2 + 's'] = shift_2 * sign else: shift_kwargs[time_unit_2 + 's'] = 1 * sign now = XArrow.now() dehumanized_instance_vanilla = Arrow.now().dehumanize( human_expression) dehumanized_instance_vanilla.microsecond = 0 dehumanized_static = XArrow.dehumanize( human_expression) now_shifted = now.shift(**shift_kwargs) dehumanized_instance = now.dehumanize(human_expression) try: assert_arrows_soft_eq(dehumanized_instance, now_shifted) assert_arrows_soft_eq(dehumanized_static, now_shifted) assert_arrows_soft_eq( dehumanized_instance, dehumanized_instance_vanilla) assert_arrows_soft_eq( dehumanized_static, dehumanized_instance_vanilla) except AssertionError: now = XArrow.now() dehumanized_instance = now.dehumanize( human_expression) now_shifted = now.shift(**shift_kwargs) assert_arrows_soft_eq(dehumanized_instance, now_shifted) dehumanized_static = XArrow.dehumanize( human_expression) assert_arrows_soft_eq(dehumanized_static, now_shifted) dehumanized_instance_vanilla = Arrow.now( ).dehumanize(human_expression) assert_arrows_soft_eq( dehumanized_instance, dehumanized_instance_vanilla) assert_arrows_soft_eq( dehumanized_static, dehumanized_instance_vanilla) # * 3 units for time_unit_3 in TIME_UNITS: if time_unit_3 == time_unit_1 or time_unit_3 == time_unit_2: continue if random() < 0.75: continue shift_3 = randint(2, 4) singular_time_unit_3 = (f"a" if time_unit_3 != "hour" else "an") + f" {time_unit_3}" plural_time_unit_3 = f"{shift_3} {time_unit_3}s" expressions = {} for fmt in [ "{0} and {1} and {2}", "{0} and {1}, {2}", "{0} and {1} {2}", "{0}, {1}, {2}", "{0}, {1} and {2}", "{0}, {1} {2}", "{0} {1} {2}", "{0} {1}, {2}", "{0} {1} and {2}", ]: for q1, q2, q3 in product(["plural", "singular"], ["plural", "singular"], ["plural", "singular"]): past_human_expression = eval( f"fmt.format({q1}_time_unit_1, {q2}_time_unit_2, {q3}_time_unit_3) + ' ago'" ) future_human_expression = eval( f"'in ' + fmt.format({q1}_time_unit_1, {q2}_time_unit_2, {q3}_time_unit_3)" ) quantity_tuple = (q1 == "plural", q2 == "plural", q3 == "plural") expressions[ past_human_expression] = quantity_tuple expressions[ future_human_expression] = quantity_tuple for human_expression, quantity_tuple in expressions.items( ): shift_kwargs = {} sign = 1 if human_expression.startswith( "in ") else -1 if quantity_tuple[0]: shift_kwargs[time_unit_1 + 's'] = shift_1 * sign else: shift_kwargs[time_unit_1 + 's'] = 1 * sign if quantity_tuple[1]: shift_kwargs[time_unit_2 + 's'] = shift_2 * sign else: shift_kwargs[time_unit_2 + 's'] = 1 * sign if quantity_tuple[2]: shift_kwargs[time_unit_3 + 's'] = shift_3 * sign else: shift_kwargs[time_unit_3 + 's'] = 1 * sign now = XArrow.now() dehumanized_instance_vanilla = Arrow.now( ).dehumanize(human_expression) dehumanized_static = XArrow.dehumanize( human_expression) now_shifted = now.shift(**shift_kwargs) dehumanized_instance = now.dehumanize( human_expression) try: assert_arrows_soft_eq(dehumanized_instance, now_shifted) assert_arrows_soft_eq(dehumanized_static, now_shifted) assert_arrows_soft_eq( dehumanized_instance, dehumanized_instance_vanilla) assert_arrows_soft_eq( dehumanized_static, dehumanized_instance_vanilla) except AssertionError: now = XArrow.now() dehumanized_instance = now.dehumanize( human_expression) now_shifted = now.shift(**shift_kwargs) assert_arrows_soft_eq(dehumanized_instance, now_shifted) dehumanized_static = XArrow.dehumanize( human_expression) assert_arrows_soft_eq(dehumanized_static, now_shifted) dehumanized_instance_vanilla = Arrow.now( ).dehumanize(human_expression) assert_arrows_soft_eq( dehumanized_instance, dehumanized_instance_vanilla) assert_arrows_soft_eq( dehumanized_static, dehumanized_instance_vanilla)