Skip to content

fix: last-used sort order and the phantom "in use" label - #990

Merged
Donkie merged 2 commits into
masterfrom
fix/last-used-null-sort
Jul 31, 2026
Merged

fix: last-used sort order and the phantom "in use" label#990
Donkie merged 2 commits into
masterfrom
fix/last-used-null-sort

Conversation

@Donkie

@Donkie Donkie commented Jul 31, 2026

Copy link
Copy Markdown
Owner

Closes #984, closes #985, closes #986 — the three reports that came in against the new client.

"Last used" sort put never-used spools on top (#984, #985)

Not a client bug. last_used is NULL until a spool is first used, and the databases disagree on where a NULL goes: PostgreSQL and CockroachDB default to NULLS FIRST on DESC, while SQLite and MySQL treat NULL as the lowest value and put it last. Both reporters run PostgreSQL, so the library's default ordering came out inverted for them.

Grouped views showed it worst, and that's what both screenshots are: a group's date is max(last_used) over its spools, so a filament holding nothing but unused spools aggregates to NULL and floated above filaments used minutes ago. The intended reading — a filament ranked by its most recently used spool, all-unused filaments at the bottom — is what this restores.

order_by_clauses() orders on an explicit expr IS NULL flag ahead of the field, so an absent value sorts last in both directions on every database. Deliberately not SQLAlchemy's nullslast(): that renders a literal NULLS LAST, which MySQL and MariaDB reject. Applied at every sort site — spools, groups, filaments, vendors, extra fields.

Note this also settles a smaller inconsistency: the NULL group key (spools with no location) now sorts last everywhere too, where the four databases used to disagree.

An unopened spool claimed to be "in use" (#986)

Two causes stacked.

Registering a spool by its measured weight computes net + spool − measured in floating point. A full 1 kg spool on a 128.11 g core gives 2.3e-13, not 0 — so used_weight === 0 was false and the spool counted as used the moment it was added. Same dust made it render 1000.0 g beside an identical spool's 1 kg. Fixed at the source (the subtraction is rounded), plus a hundredth-of-a-gram tolerance on unused so spools already carrying dust read correctly without touching anyone's database, and weightAuto rounds before picking its unit.

The label was independently wrong. "in use" was the fallback for "used, but no date on record", and it asserts something Spoolman cannot know — there is no live link to a printer, no notion of a spool being mounted. A reader with nothing printing quite reasonably read it as "printing right now". It now says a plain "used", through one shared usageLabel() so the row, spool inspector and filament inspector cannot drift apart. library.inUse is dropped and library.used added to the English source only; Weblate handles the other 31 locales.

Verification

  • All four databases: 438 integration tests pass on SQLite, PostgreSQL, MariaDB and CockroachDB.
  • The new tests fail without the fix: on PostgreSQL, test_group_sort_last_used_puts_unused_groups_last[desc] puts the all-unused group first — the reported bug — and test_find_spools_sort_last_used_puts_never_used_last[desc] fails likewise. The [asc] variants guard the SQLite/MariaDB direction.
  • Confirmed against real engines that ORDER BY (expr IS NULL) ASC, expr DESC is accepted and correct on PostgreSQL, MariaDB and SQLite.
  • 60 client unit tests, svelte-check clean, ruff and 188 backend unit tests clean.

Worth your call

NULLs now sort last in both directions. Ascending "last used" on SQLite previously led with never-used spools; it now trails with them. I took consistency across the four databases over preserving the old per-database behaviour, on the reading that "no date recorded" is neither the newest nor the oldest value — but say the word if you'd rather ascending kept them first.

https://claude.ai/code/session_01EaLK21WpQYDE1DkKwZHK3P

Donkie added 2 commits July 31, 2026 14:38
Sorting the library by "last used" led with everything that had never been
used, on PostgreSQL and CockroachDB. Those two default to NULLS FIRST on
DESC, while SQLite and MySQL treat NULL as the lowest value and put it last;
`last_used` is NULL until a spool is first used, so the default library
ordering came out inverted for half the supported databases (#984, #985).
Grouped views showed it worst: a group's date is the max over its spools, so
a filament holding nothing but unused spools aggregates to NULL and floated
to the top, above filaments used minutes ago.

order_by_clauses() now orders on an explicit "is it null" flag ahead of the
field itself, so an absent value sorts last in both directions everywhere.
It is written as `expr IS NULL` rather than SQLAlchemy's nullslast(), which
renders a literal NULLS LAST that MySQL and MariaDB reject. Applied to every
sort site -- spools, groups, filaments, vendors, extra fields -- so ordering
no longer depends on which database is underneath.

Verified against all four: 438 integration tests pass on SQLite, PostgreSQL,
MariaDB and CockroachDB, and both new regression tests fail on PostgreSQL
without the fix.

Claude-Session: https://claude.ai/code/session_01EaLK21WpQYDE1DkKwZHK3P
A spool still in its vacuum pack, with no first- or last-used date, showed
"in use" in the library and its inspector (#986). Two separate causes.

Registering a spool by its measured weight computes `net + spool - measured`
in floating point, and a full 1 kg spool on a 128.11 g core lands on 2.3e-13
rather than 0 -- so `used_weight === 0` was false and the spool counted as
used from the moment it was added. It also rendered "1000.0 g" where its
identical neighbour showed "1 kg". The subtraction is now rounded at the
source, `unused` allows a hundredth of a gram (under any scale's resolution,
and under the display's, so nothing real is rounded away), and weightAuto
rounds before choosing its unit. Existing spools already carrying dust read
correctly again without touching the database.

The label itself was also wrong. "in use" was the fallback for "used, but no
date on record", and it claims something Spoolman cannot know: there is no
live link to a printer and no notion of a spool being mounted. It now reads
a plain "used", via one shared usageLabel() so the row, the spool inspector
and the filament inspector can no longer drift apart.

Claude-Session: https://claude.ai/code/session_01EaLK21WpQYDE1DkKwZHK3P
@Donkie
Donkie marked this pull request as ready for review July 31, 2026 14:46
@Donkie
Donkie merged commit 09c2970 into master Jul 31, 2026
22 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant