Add number of games filter to purchases
This commit is contained in:
@@ -847,6 +847,16 @@ def PurchaseFilterBar(
|
|||||||
except Exception:
|
except Exception:
|
||||||
price_range_min, price_range_max = 0, 100
|
price_range_min, price_range_max = 0, 100
|
||||||
|
|
||||||
|
num_min, num_max = _parse_range(existing, "num_purchases")
|
||||||
|
try:
|
||||||
|
num_aggregate = Purchase.objects.aggregate(
|
||||||
|
num_min=models.Min("num_purchases"), num_max=models.Max("num_purchases")
|
||||||
|
)
|
||||||
|
num_range_min = max(int(num_aggregate.get("num_min") or 0), 0)
|
||||||
|
num_range_max = max(int(num_aggregate.get("num_max") or 10), 1)
|
||||||
|
except Exception:
|
||||||
|
num_range_min, num_range_max = 0, 10
|
||||||
|
|
||||||
fields = [
|
fields = [
|
||||||
Component(
|
Component(
|
||||||
tag_name="div",
|
tag_name="div",
|
||||||
@@ -912,5 +922,16 @@ def PurchaseFilterBar(
|
|||||||
min_placeholder="0.00",
|
min_placeholder="0.00",
|
||||||
max_placeholder="100.00",
|
max_placeholder="100.00",
|
||||||
),
|
),
|
||||||
|
RangeSlider(
|
||||||
|
label="Games in purchase",
|
||||||
|
input_name_prefix="filter-num-purchases",
|
||||||
|
min_value=num_min,
|
||||||
|
max_value=num_max,
|
||||||
|
range_min=num_range_min,
|
||||||
|
range_max=num_range_max,
|
||||||
|
step="1",
|
||||||
|
min_placeholder="e.g. 1",
|
||||||
|
max_placeholder="e.g. 5",
|
||||||
|
),
|
||||||
]
|
]
|
||||||
return _filter_bar(fields, filter_json, preset_list_url, preset_save_url)
|
return _filter_bar(fields, filter_json, preset_list_url, preset_save_url)
|
||||||
|
|||||||
@@ -130,6 +130,17 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Purchase-specific: num_purchases ──
|
||||||
|
var numGamesMin = numberValue(form, "filter-num-purchases-min");
|
||||||
|
var numGamesMax = numberValue(form, "filter-num-purchases-max");
|
||||||
|
if (numGamesMin !== "" && numGamesMax !== "") {
|
||||||
|
filter.num_purchases = criterion(parseInt(numGamesMin, 10), parseInt(numGamesMax, 10), "BETWEEN");
|
||||||
|
} else if (numGamesMin !== "") {
|
||||||
|
filter.num_purchases = criterion(parseInt(numGamesMin, 10), null, "GREATER_THAN");
|
||||||
|
} else if (numGamesMax !== "") {
|
||||||
|
filter.num_purchases = criterion(parseInt(numGamesMax, 10), null, "LESS_THAN");
|
||||||
|
}
|
||||||
|
|
||||||
if (mastered && mastered.checked) {
|
if (mastered && mastered.checked) {
|
||||||
filter.mastered = criterion(true, null, "EQUALS");
|
filter.mastered = criterion(true, null, "EQUALS");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -563,3 +563,70 @@ class TestFilterBarRendering:
|
|||||||
platform_section = html[platform_start:]
|
platform_section = html[platform_start:]
|
||||||
# Should have at least one modifier option
|
# Should have at least one modifier option
|
||||||
assert "(Any)" in platform_section or "(None)" in platform_section
|
assert "(Any)" in platform_section or "(None)" in platform_section
|
||||||
|
|
||||||
|
|
||||||
|
class TestPurchaseNumPurchasesAgainstDB:
|
||||||
|
"""num_purchases IntCriterion filters purchases by game count."""
|
||||||
|
|
||||||
|
def _seed(self):
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
from games.models import Game, Platform, Purchase
|
||||||
|
|
||||||
|
platform, _ = Platform.objects.get_or_create(name="Test", icon="test")
|
||||||
|
a, _ = Game.objects.get_or_create(name="A", defaults={"platform": platform})
|
||||||
|
b, _ = Game.objects.get_or_create(name="B", defaults={"platform": platform})
|
||||||
|
c, _ = Game.objects.get_or_create(name="C", defaults={"platform": platform})
|
||||||
|
|
||||||
|
single = Purchase.objects.create(
|
||||||
|
platform=platform, date_purchased=datetime.date(2024, 1, 1)
|
||||||
|
)
|
||||||
|
single.games.set([a])
|
||||||
|
|
||||||
|
double = Purchase.objects.create(
|
||||||
|
platform=platform, date_purchased=datetime.date(2024, 1, 1)
|
||||||
|
)
|
||||||
|
double.games.set([a, b])
|
||||||
|
|
||||||
|
triple = Purchase.objects.create(
|
||||||
|
platform=platform, date_purchased=datetime.date(2024, 1, 1)
|
||||||
|
)
|
||||||
|
triple.games.set([a, b, c])
|
||||||
|
|
||||||
|
return {"single": single, "double": double, "triple": triple}
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_between_two_and_three(self):
|
||||||
|
from games.filters import PurchaseFilter
|
||||||
|
from games.models import Purchase
|
||||||
|
|
||||||
|
seeded = self._seed()
|
||||||
|
pf = PurchaseFilter.from_json(
|
||||||
|
{"num_purchases": {"value": 2, "value2": 3, "modifier": "BETWEEN"}}
|
||||||
|
)
|
||||||
|
result = set(Purchase.objects.filter(pf.to_q()))
|
||||||
|
assert result == {seeded["double"], seeded["triple"]}
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_greater_than_one(self):
|
||||||
|
from games.filters import PurchaseFilter
|
||||||
|
from games.models import Purchase
|
||||||
|
|
||||||
|
seeded = self._seed()
|
||||||
|
pf = PurchaseFilter.from_json(
|
||||||
|
{"num_purchases": {"value": 1, "modifier": "GREATER_THAN"}}
|
||||||
|
)
|
||||||
|
result = set(Purchase.objects.filter(pf.to_q()))
|
||||||
|
assert result == {seeded["double"], seeded["triple"]}
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_equals_one(self):
|
||||||
|
from games.filters import PurchaseFilter
|
||||||
|
from games.models import Purchase
|
||||||
|
|
||||||
|
seeded = self._seed()
|
||||||
|
pf = PurchaseFilter.from_json(
|
||||||
|
{"num_purchases": {"value": 1, "modifier": "EQUALS"}}
|
||||||
|
)
|
||||||
|
result = set(Purchase.objects.filter(pf.to_q()))
|
||||||
|
assert result == {seeded["single"]}
|
||||||
|
|||||||
Reference in New Issue
Block a user