Commit Graph
66 Commits
Author SHA1 Message Date
Donkie 29f0f362d4 client_v2: add multi-color filament support
Add a shared ColorEditor component (single/multi toggle, per-color
pickers + hex fields, add/remove colors, illustrated coaxial vs
longitudinal cards that preview the actual colors) and wire it into
both the add-spool create form and the library filament inspector,
which also gains its first color picker.

Colors and multi_color_direction round-trip through the API via a new
colorFieldsToApi helper that keys single- vs multi-color by count and
nulls out the other fields, so switching between them stays consistent.

Make color type filterable in the library toolbar (Single / Coextruded
/ Longitudinal) backed by a new additive filament.multi_color_direction
query param on the spool and spool/group endpoints; the Single option
sends an empty value to match single-color (NULL) filaments.

Also tidy a few inspector field labels to reuse the canonical
filament.fields.* / vendor.fields.* message keys.

Claude-Session: https://claude.ai/code/session_01VQQtwPBPiMCoZCpBbbqS64
2026-07-18 23:42:53 +02:00
Donkie d7486d5d51 Improved filtering/sorting. Allows filtering/sorting by extra fields. 2026-07-18 15:54:58 +02:00
Donkie c82e3911e1 WIP: add v2 client (Svelte) with spool grouping backend
Introduces client_v2, a new SvelteKit-based client, alongside the
backend it depends on: a /spool/group endpoint that returns
per-group aggregates (count, in-use count, remaining weight,
last-used) grouped by filament, vendor, material or location, so the
client can paginate whole groups instead of fetching every spool.

Not yet complete/wired up for release.
2026-07-12 19:21:34 +02:00
470d8f3bcc Add filtering and sorting for custom fields (#773)
* Add filtering and sorting for custom fields

* Fix tests and make code postgres compatible

* Fix bugs and expand test coverage for custom field filter/sort

* trigger CI

* fix: address extra field filter review feedback

* fix: clean up custom field table state handling

* docs: update extra field table view description

* fix: narrow boolean custom field filter syntax

* Expand test coverage for all custom field types/entities and fix range filter/sort

- Add comprehensive tests covering all 9 field types (text, integer, float,
  boolean, single-choice, multi-choice, datetime, integer_range, float_range)
  for filter and sort on spool, filament, and vendor entities
- Add invalid-filter 400 tests for float, integer_range, and float_range
- Fix integer_range/float_range filter to use LIKE pattern matching against
  Python's deterministic json.dumps output instead of fragile .contains()
- Add integer_range/float_range sort support via a @compiles helper
  (_JsonArrayFirstElement) that emits CAST(col AS JSON)->>0 on PostgreSQL
  and JSON_EXTRACT(col, '$[0]') on SQLite/MariaDB
- Add logger and __all__ to extra_fields.py (aligns with PR #893)
- All tests verified passing on postgres, sqlite, and mariadb

* Add filter UI for all custom field types and fix range filters

- Add text input filter dropdown for text fields (substring search)
- Add range input filter dropdowns (min/max) for integer, float, integer_range, and float_range fields
- Add datetime picker filter dropdown for datetime fields
- Fix boolean "No" filter to use empty value so it correctly matches unset/false fields
- Fix integer_range/float_range filter to use numeric comparisons instead of LIKE-based exact matching (stored_min >= filter_min, stored_max <= filter_max)
- Add range filter support for integer/float fields (min:max format)
- Add _JsonArraySecondElement cross-database helper for extracting second JSON array element

* Range filter for integer/float fields and fix range_field filter semantics

Backend:
- Add _JsonArraySecondElement cross-database helper (mirrors _JsonArrayFirstElement)
- integer_range/float_range filter now uses numeric comparisons:
  stored_min >= filter_min and stored_max <= filter_max (was LIKE exact match)
- integer/float filter now supports min:max range format in addition to exact match:
  stored_value >= min and/or stored_value <= max

Frontend:
- integer and float fields now use a range filter dropdown (min/max inputs)
  instead of a single exact-value input

Tests:
- Fix integer_range/float_range spool and vendor tests broken by new >= semantics
  (filter values updated to discriminate between test entries)
- Add range filter tests (min only, max only, both) for integer and float fields

* Replace 12 copy-pasted numeric field tests with 4 parametrized tests

Introduce a _create_entity helper and @pytest.mark.parametrize("entity_type",
["spool", "filament", "vendor"]) to run each numeric field type test against
all three entity types from a single test function.

- Removes 12 individual tests (integer/float/integer_range/float_range × 3 entities)
- Adds 4 parametrized tests covering the same ground plus the previously missing
  min-only / max-only range filter cases for filament and vendor
- Uses try/finally for cleanup so entities are always deleted even on assertion failure

* Add datetime range filter and restore integer/float range filter support

- Backend: add datetime range filter using '|' separator (ISO dates contain ':')
- Backend: restore integer/float range filter support (lost during attribution rewrite)
- Frontend: replace single DateTimePicker with From/To range pickers for datetime fields

* Use local timezone in filter picker; add range filter tests

The DateTimeRangeFilterDropdown was initializing pickers with dayjs.utc(),
causing them to display and accept UTC times directly. The entry form shows
local time and converts to UTC, so the filter was inconsistent: entering
the same displayed time would produce a filter value 2h offset from what
was stored (in UTC+2), making exact boundaries fail unexpectedly.

Fix: initialize picker values with dayjs() (local mode) so the filter picker
behaves the same as the entry form — shows local times and converts to UTC.

Tests: replace 3 separate entity-specific datetime tests with one parametrized
test covering spool/filament/vendor, and add range filter cases (start|, |end,
start|end).

* Replace all entity-specific tests with parametrized tests

Replace 16 separate spool/filament/vendor tests for text, boolean,
single-choice, multi-choice, and empty-filter with 5 parametrized tests
that run against all three entity types. Each test is now defined once
and executed 3 times, eliminating copy-paste and ensuring consistent
coverage across all entity types.

Parametrize invalid-filter-value 400 tests across all entity types

The non-numeric-value error uses "Invalid integer/float range filter value"
while the missing-colon error uses "Invalid range filter value". Both contain
"range filter value", so use that as the assertion substring.

* Align custom filter dropdowns with Ant Design choice filter style

All three custom filter dropdowns (text, number range, datetime range) now
match the built-in choice filter footer: a border-top separator, Reset as a
link button on the left (disabled when no filter is active), and OK as a
primary button on the right.

* fix(fields): correct custom-field filter edge cases across databases

- Float exact-match filters now compare numerically (cast to Float) instead of
  by JSON string, so float fields stored as integer JSON (e.g. "2") or
  non-canonical decimals (e.g. "2.50") match an equivalent filter.
- integer_range/float_range filter and sort no longer return HTTP 500 on
  CockroachDB: the PostgreSQL '->>' JSON extraction is now also registered for
  the cockroachdb dialect, which previously fell back to the unsupported
  JSON_EXTRACT function.
- Text exact-match and single-choice equality use ensure_ascii=False so
  non-ASCII values match how the frontend's JSON.stringify stores them.

Co-authored-by: Dieter Blomme <dieterblomme@gmail.com>

* test(fields): expand custom-field filter/sort coverage

Add tests covering filter composition (multiple extra fields, extra + built-in,
filter one field / sort another), pagination total-count, unknown-field
handling, text case-insensitivity/substring/non-ASCII, numeric multi-value OR,
boolean empty semantics and token rejection, multi-choice substring collisions,
empty filters on numeric fields, null-bounded ranges, and additional
invalid-value 400 paths.

Verified against sqlite, postgres, mariadb and cockroachdb.

Co-authored-by: Dieter Blomme <dieterblomme@gmail.com>

* style(client): format column.tsx with prettier

The custom-field filter dropdowns' Reset buttons were written as one-line
onClick handlers that prettier wraps across multiple lines. Apply prettier
so the CI style check passes.

Co-authored-by: Dieter Blomme <dieterblomme@gmail.com>

* fix(fields): decode JSON scalars in-DB for text/choice/datetime custom-field filters

Match text, single-choice and datetime custom-field filters against the
database-decoded JSON scalar (new _JsonScalarText cross-dialect helper:
#>> '{}' on postgres/cockroachdb, JSON_UNQUOTE(JSON_EXTRACT) on mysql,
json_extract on sqlite) instead of reconstructing the exact JSON
serialization the client wrote. This decouples matching from
json.dumps/JSON.stringify encoding quirks (non-ASCII escaping, surrounding
quotes). Also escape LIKE wildcards so '%' and '_' in text and multi-choice
queries match literally rather than acting as wildcards.

Verified against sqlite, postgres, mariadb and cockroachdb.

Co-authored-by: Dieter Blomme <dieterblomme@gmail.com>

* test(fields): cover LIKE-wildcard escaping in custom-field filters

Add parametrized tests (spool/filament/vendor) asserting that '%' and '_'
are matched literally in text and multi-choice filters, that a literal '/'
(the internal LIKE escape char) matches literally, and that single-choice
equality handles a '%' value exactly.

Co-authored-by: Dieter Blomme <dieterblomme@gmail.com>

---------

Co-authored-by: Donkie <daniel.cf.hultgren@gmail.com>
Co-authored-by: akira69 <akira69@gmail.com>
2026-07-08 10:14:27 +02:00
Donkie 9b6af119c6 Fix commit-before-notify races in write paths (flaky postgres deletes)
Several DB write functions mutated and then sent their websocket notification (or
just returned) without committing, relying on the request session's teardown
commit. On async postgres that commit races the HTTP response, so a fast
follow-up read could still see the pre-write state — e.g. `DELETE /vendor/{id}`
returns 200 but an immediate `GET` still returns 200. Latent since 2023 (a6d527a);
the async-timing dependency bump widened the window enough to intermittently fail
`test (postgres)` in CI.

Commit inline before notifying, matching the functions that already did
(filament.delete, vendor/spool create/update) and the commit-first invariant from
the earlier websocket-ordering fix:
- vendor.delete, spool.delete
- setting.update, setting.delete
- vendor/filament/spool.clear_extra_field
- spool.rename_location

(use/measure already commit via their wrappers; reads are unaffected.)

Verified via `SPOOLMAN_CONTAINER_ENGINE=podman poe itest`: full 4-DB suite green
plus 7/7 repeat postgres runs with zero flakes (was ~66% failing before).
2026-07-06 15:53:40 +02:00
Donkie b942d1f8a1 Replaced pdm with uv, updated backend deps, python 3.10 minimum, simplified install script 2026-01-07 22:01:02 +01:00
Thorsten Lanfer 55acc4993d reformat 2025-03-05 19:46:54 +01:00
Thorsten Lanfer 4b8fd3aed3 Update spool.py 2025-02-24 22:21:10 +01:00
Thorsten Lanfer 28f6f8e916 Allow posting partial updates of extra attributes 2025-02-24 22:21:10 +01:00
Donkie 37b6d1fcee Added ability to rename location 2024-11-20 21:00:39 +01:00
Donkie 86f69d470f Added backend supported csv/json export functionality 2024-10-27 12:36:39 +01:00
Donkie 32c737fa98 Revert so commit now happens before notice, but add a catch-all to websocket notifications 2024-08-16 15:13:09 +02:00
Donkie e5f79493cc Fixed error on trying to send ws message to disconnected client
This could result in a major filament usage track failure since the Use http request would fail and Moonraker would keep retrying to save the used filament. However it was actually saved since the db.commit() happens before the websocket message fails.
2024-08-16 10:28:22 +02:00
Donkie 535fa40ad2 Updated most python dependencies
Primarily FastAPI and Pydantic to v2. Also ruff to latest.

Updated some code to support Pydantic v2
2024-05-23 19:42:27 +02:00
Donkie ca545b72b9 Fixed spool price sorting not working
Resolves #349
2024-05-20 22:42:44 +02:00
Donkie c4693d3f34 Implemented used_length spool sort 2024-05-15 20:18:24 +02:00
Donkie 51c3b46f1f Fixed wrong remaining weight and length spool sort 2024-05-15 20:17:00 +02:00
Matt Gerega fdec032d19 Updated handling of higher measurements 2024-04-15 16:41:14 -04:00
Matt Gerega c6f9abeced Changed initial_weight to be net weight, not gross weight 2024-04-10 11:00:46 -04:00
Matt Gerega 23cfb42ba8 Updated based on PR requests 2024-04-08 17:38:18 -04:00
Matt Gerega 37804c14a8 modified default settings 2024-04-08 09:27:15 -04:00
Matt Gerega a6ae808389 Added additional integration tests for measure and add 2024-03-28 09:29:39 -04:00
Matt Gerega 19c414da4b Add integration tests for new measure endpoint 2024-03-27 15:44:54 -04:00
Matt Gerega 3ea4937f24 Updated spool UIs (create/edit) 2024-03-27 14:47:05 -04:00
Matt Gerega e68f88da2a Added empty spool weight as an option field for the vendor 2024-03-26 20:35:50 -04:00
Matt Gerega 83fd3073ec Fixed formatting 2024-03-26 14:02:17 -04:00
Matt Gerega 1d6830d769 Added initial_weight and empty_weight to spool 2024-03-26 13:55:44 -04:00
Donkie c2335366bb Fixed sorting by remaining weight/length 2024-02-02 22:10:11 +01:00
Donkie 290eb5cb81 Added backend ability to sort by combined_name 2024-01-30 18:20:53 +01:00
Donkie 09b8705133 Existing extra fields for entities are now cleared if a field is deleted 2024-01-23 18:58:57 +01:00
Donkie 77ac9d6a5c Added custom extra fields to backend
No integration tests yet, TBD
2024-01-23 18:58:57 +01:00
pdsccode d18d21d8fd Fixed db access to spool price field 2023-12-30 09:18:37 +01:00
pdsccode 1eb78f01df Added pricing field for single spool 2023-12-30 07:29:27 +01:00
Donkie 5f7a063125 Added resource field in websocket message
Used to differentiate resources for root-level subscriptions
2023-10-15 20:39:53 +02:00
Donkie a6d527aaf0 Added/deleted-type websocket messages
Events are now sent for not only when an object changes but also when one is created or deleted.

For now it's not really possible to subscribe to these events however.

This breaks backwards-compatibility with the websocket messages.
2023-10-15 19:46:10 +02:00
Donkie 9595aec675 Fixed registered field having wrong timezone
This happened with at least Postgres, which took it's server's timezone. It's complicated. Let's just set it from our side instead where we have some control.
2023-10-02 21:36:50 +02:00
Donkie 205574fa2c Fixed timezone being returned incorrectly
Happened if you've set TZ to non-UTC.

Apparently astimezone and replace doesn't do the same thing even if tzinfo is None.
2023-10-02 21:36:50 +02:00
Donkie cadb707fdd Added WebSocket notify API for spools 2023-09-17 22:00:00 +02:00
Donkie 6ee3b8df70 Added support for multiple int where's 2023-09-16 11:13:30 +02:00
Donkie 841cc8b6df Added sort by remaining weight 2023-09-16 11:13:30 +02:00
Donkie a4d14c5b52 Added X-Total-Count test asserts
Fixed issue with incorrect total count for vendors
2023-09-16 11:13:30 +02:00
Donkie f47eacd2a9 Added support for finding empty vendor fields 2023-09-16 11:13:30 +02:00
Donkie de16dd7be0 Added database support for finding empty fields 2023-09-16 11:13:30 +02:00
Donkie e2d937aaf1 Added x-total-count response header 2023-09-16 11:13:30 +02:00
Donkie 557ea82171 Added endpoints for getting list of materials, etc 2023-09-16 11:13:30 +02:00
Donkie 9d51ee49ed Serverside sort and limit for vendor and filaments 2023-09-16 11:13:30 +02:00
Donkie b9f1b71c1f Serverside spool find limit and offset 2023-09-16 11:13:30 +02:00
Donkie 4fdea75e3f Serverside spool sorting 2023-09-16 11:13:30 +02:00
Donkie 871e5282e5 Updated python packages 2023-08-28 21:10:07 +02:00
Donkie 6e7d9a2e03 Replaced db.flush with db.commit
To try fix some odd cases of greenlet whining
2023-08-28 20:31:25 +02:00