Say the date and the time, and sort by both
The listing gave the time and not the date, which is fine for an evening's scanning and useless for a directory that has been filling for a fortnight: two recordings a week apart looked like neighbours. Every moment in the browser is now written YY-mm-dd hh:mm:ss am/pm -- date first so a column of them reads down in order, twelve-hour so it reads the way it would be said. Midnight is 12 am and noon is 12 pm, which is the pair a twelve-hour clock gets wrong when it is done by subtraction, so both are tested. The sort is named "date/time" rather than "time" and orders by the whole moment, with the filename breaking a tie so that the list does not shuffle itself between one reload and the next. --sort time still works; it is the sort of thing that lives in a shell alias. The date costs twelve columns, so the frequency column is now only as wide as the widest frequency in the list rather than always wide enough for 1090.000001 MHz. It is fixed for the whole list, not the screenful, because a column that resizes as the list scrolls under it makes the whole listing appear to twitch. --list is left in ISO and twenty-four hours: it is there to be piped into grep and sort, and those want the format that sorts as text. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016PsWPTweCT6pwxKngvVxcg
This commit is contained in:
parent
3d7f76118e
commit
0f378c4d6c
7 changed files with 178 additions and 27 deletions
|
|
@ -377,7 +377,7 @@ def test_the_cursor_stays_on_screen_in_a_long_list(tmp_path):
|
|||
|
||||
def test_sorting_cycles_and_reorders(library):
|
||||
b = browser(library)
|
||||
assert b.sort == "time"
|
||||
assert b.sort == "date/time"
|
||||
b.handle("s")
|
||||
assert b.sort == "frequency"
|
||||
assert [c.frequency for c in b.view] == sorted(c.frequency
|
||||
|
|
@ -387,6 +387,85 @@ def test_sorting_cycles_and_reorders(library):
|
|||
assert b.view[0].duration >= b.view[-1].duration
|
||||
|
||||
|
||||
def test_the_listing_gives_the_date_as_well_as_the_time(library):
|
||||
"""Without the date, two recordings a week apart look like neighbours."""
|
||||
text = frame(browser(library))
|
||||
assert re.search(r"\d\d-\d\d-\d\d \d\d:\d\d:\d\d [ap]m", text)
|
||||
|
||||
|
||||
def test_the_clock_is_twelve_hour_with_midnight_and_noon_named(tmp_path):
|
||||
"""The two hours a twelve-hour clock gets wrong if it is done by
|
||||
subtraction: midnight is 12 am and noon is 12 pm, not 0 am and 0 pm."""
|
||||
make_capture(tmp_path, 146.52, "2026-08-30_00_00_30", "nfm")
|
||||
make_capture(tmp_path, 146.52, "2026-08-30_12_00_30", "nfm")
|
||||
make_capture(tmp_path, 146.52, "2026-08-30_13_05_00", "nfm")
|
||||
text = frame(browser(tmp_path))
|
||||
assert "26-08-30 12:00:30 am" in text
|
||||
assert "26-08-30 12:00:30 pm" in text
|
||||
assert "26-08-30 01:05:00 pm" in text
|
||||
|
||||
|
||||
def test_sorting_by_date_time_is_chronological_across_every_boundary(tmp_path):
|
||||
"""Year, month, day, then hour, minute, second. The order below is the
|
||||
one a clock would put them in; the browser has to agree with it whichever
|
||||
way the filenames happen to sort as text."""
|
||||
moments = ["2025-12-31_23_59_59", "2026-01-01_00_00_01",
|
||||
"2026-01-01_00_00_02", "2026-01-31_09_00_00",
|
||||
"2026-02-01_08_00_00", "2026-08-30_11_59_59",
|
||||
"2026-08-30_12_00_00", "2026-08-30_13_00_00"]
|
||||
for i, when in enumerate(moments):
|
||||
make_capture(tmp_path, 146.52 + i * 0.01, when, "nfm")
|
||||
b = browser(tmp_path)
|
||||
assert [c.when.strftime("%Y-%m-%d_%H_%M_%S") for c in b.view] == \
|
||||
list(reversed(moments))
|
||||
|
||||
|
||||
def test_two_signals_in_the_same_second_keep_a_settled_order(tmp_path):
|
||||
"""A tie is broken by the filename so that the list does not shuffle
|
||||
itself between one reload and the next."""
|
||||
make_capture(tmp_path, 146.52, "2026-08-30_10_00_00", "nfm")
|
||||
make_capture(tmp_path, 145.00, "2026-08-30_10_00_00", "nfm")
|
||||
first = [c.path.name for c in browser(tmp_path).view]
|
||||
assert first == sorted(first)
|
||||
|
||||
|
||||
def test_the_sort_key_is_called_date_time(library):
|
||||
b = browser(library, width=140)
|
||||
assert b.sort == "date/time"
|
||||
assert "date/time" in frame(b)
|
||||
|
||||
|
||||
def test_the_old_name_for_the_time_sort_still_works(library, monkeypatch):
|
||||
"""--sort time is the sort of thing that lives in a shell alias."""
|
||||
from bandsaunter import browse
|
||||
|
||||
seen = {}
|
||||
|
||||
def fake_loop(self):
|
||||
seen["sort"] = self.sort
|
||||
return 0
|
||||
|
||||
monkeypatch.setattr(browse.Browser, "run", fake_loop, raising=False)
|
||||
monkeypatch.setattr(browse.Console, "is_terminal", property(lambda s: True))
|
||||
assert main(["--sort", "time", str(library)]) == 0
|
||||
assert seen["sort"] == "date/time"
|
||||
|
||||
|
||||
def test_the_frequency_column_does_not_change_width_while_scrolling(tmp_path):
|
||||
"""One wide frequency in the list fixes the column for the whole list, so
|
||||
that scrolling past it does not make everything else jump sideways."""
|
||||
make_capture(tmp_path, 1090.000001, "2026-08-30_10_00_00", "nfm")
|
||||
for i in range(40):
|
||||
make_capture(tmp_path, 146.52, f"2026-08-30_11_{i // 60:02d}_{i % 60:02d}",
|
||||
"nfm")
|
||||
b = browser(tmp_path, height=24)
|
||||
frame(b)
|
||||
was = b.freq_width
|
||||
b.handle("end")
|
||||
frame(b)
|
||||
assert b.freq_width == was
|
||||
|
||||
|
||||
def test_quitting_stops_the_loop(library):
|
||||
assert browser(library).handle("q") is False
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue