Commit Graph

13 Commits

Author SHA1 Message Date
lukas ab4dae55eb feat(filters): date-range filtering on PlayEventFilter.started/ended (#67)
Change PlayEventFilter.started/ended from StringCriterion to DateCriterion
so they support GREATER_THAN / LESS_THAN / BETWEEN, enabling
"finished in year Y" to be expressed through the filter system.

PlayEvent.started/ended are DateField columns, so the criteria apply with
bare field names (no __date lookup, unlike SessionFilter.timestamp_start
which is a datetime). This mirrors the existing PurchaseFilter DateField
precedent. Deserialization auto-switches via the field annotation and the
serialized JSON shape is unchanged, so the type change is backward-compatible.

Prerequisite for #65 Tier-2 stats-page filtered links. Part of #61.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 11:47:39 +02:00
lukas 3fd02bbcf9 feat(filters): programmatic filter links + navbar playtime links (#56)
Add filter_url(), a reverse()-style helper that builds a URL to a filtered
list view from a filter object (target inferred from the filter type).

Add OperatorFilter.where(**lookups), a Django-.filter()-style ergonomic
constructor that resolves each field's criterion class from its annotation
(shared with from_json via _criterion_class_for, removing duplication).

Make SessionFilter.timestamp_start/timestamp_end DateCriterion applied via
the __date lookup, so date ranges over the timestamp columns are expressible.

Wire the navbar 'today' / 'last 7 days' totals as links to the matching
filtered session lists, and align the 'last 7 days' total to the same
calendar-day window so the number matches the list it links to.

Stats-table and game-detail links remain a follow-up (see spec).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RF5L4HtbcykTfY9YUYGds3
2026-06-21 08:53:06 +02:00
lukas c7af814364 Clear pre-existing ruff lint + format debt (make check now green) 2026-06-13 21:27:46 +02:00
lukas 15a97dee9a Use hours instead of minutes for playtime filters 2026-06-12 22:45:25 +02:00
lukas 19f1cdd197 feat: migrate playevent_note to StringCriterion and add note string filter to SessionFilterBar 2026-06-12 22:45:25 +02:00
lukas 03adcf99a7 Implement date filters in purchase list 2026-06-12 22:45:25 +02:00
lukas c2996fd91b Add more filters 2026-06-12 22:45:25 +02:00
lukas b8d807d302 feat: implement comprehensive filters and cross-entity queries 2026-06-12 22:45:25 +02:00
lukas 737dd9275b Fix includes all query returning duplicates 2026-06-09 08:47:20 +02:00
lukas 103219a5e7 Add includes only matcher mode 2026-06-09 08:47:20 +02:00
Claude ba9b92d419 Align set-criterion modifiers with Stash (any/all/none) and harmonize EXCLUDES
Closes #10.

Backend (common/criteria.py):
- Treat `excludes` as an always-orthogonal AND'd negative across both
  MultiCriterion and ChoiceCriterion; the modifier now governs only the
  `value` (include) set. This removes the prior divergence where
  MultiCriterion.EXCLUDES dropped the excludes list and ChoiceCriterion.EXCLUDES
  swapped include/exclude into a positive.
- Fold INCLUDES / INCLUDES_ALL / EXCLUDES (+ EQUALS/NOT_EQUALS aliases) into the
  shared _SetCriterion base so the two subclasses cannot drift; remove _extra_q.

M2M "has all" (games/filters.py):
- PurchaseFilter._games_to_q builds a pk__in subquery with one join per value so
  INCLUDES_ALL on the many-to-many games field works in a single .filter()
  (a naive Q(games=a) & Q(games=b) collapses to one join and matches nothing).

UI (FilterSelect + filter_bar.js):
- Add an optional any/all/none match-mode <select> (INCLUDES/INCLUDES_ALL/
  EXCLUDES) rendered before the pills via a new `leading` slot on the shared
  combobox shell. A native control so its value is its state. readSearchSelect
  serialises it to data-match; filter_bar folds it into the criterion modifier.
  Orthogonal to the (Any)/(None) presence pseudo-options and the exclude channel.
- Enable it for the M2M Purchase.games field (INCLUDES_ALL is only meaningful
  for multi-valued relations). Styled with already-compiled utilities.

Tests: harmonized EXCLUDES + INCLUDES_ALL for both criterion types, a DB-backed
INCLUDES_ALL vs INCLUDES contrast on Purchase.games, and FilterSelect /
PurchaseFilterBar rendering + round-trip of the match mode.

https://claude.ai/code/session_01KwVrGFbq13mZdhDL9G6zhg
2026-06-09 08:47:20 +02:00
lukas ed8589a972 Fix more code smells
Django CI/CD / test (push) Successful in 39s
Django CI/CD / build-and-push (push) Successful in 1m19s
2026-06-06 13:14:55 +02:00
lukas b6864e59ce Add filters
Django CI/CD / test (push) Successful in 43s
Django CI/CD / build-and-push (push) Successful in 1m22s
2026-06-06 12:13:04 +02:00