Skip to content

Commit 7a9bc61

Browse files
authored
Merge pull request #111 from Siege-Software/change-date-parse
update ubuntu image
2 parents 8ca39b9 + 2417145 commit 7a9bc61

8 files changed

Lines changed: 102 additions & 22 deletions

File tree

.github/workflows/build.yml

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,11 @@ on: # yamllint disable-line rule:truthy
99
jobs:
1010
build:
1111
name: Python ${{ matrix.python-version }} | Django ${{ matrix.django-version}} | Ubuntu
12-
runs-on: ubuntu-20.04
12+
runs-on: ubuntu-22.04
1313
strategy:
1414
fail-fast: false
1515
matrix:
1616
include:
17-
- python-version: 3.9
18-
django-version: "3.2.*"
19-
- python-version: 3.9
20-
django-version: "4.0.*"
21-
- python-version: 3.9
22-
django-version: "4.1.*"
23-
2417
- python-version: "3.10"
2518
django-version: "3.2.*"
2619
- python-version: "3.10"
@@ -52,6 +45,19 @@ jobs:
5245
typesense-version: '27.0'
5346
typesense-api-key: sample_key
5447

48+
- name: Wait for TypeSense to be ready
49+
run: |
50+
echo "Waiting for TypeSense..."
51+
for i in {1..30}; do
52+
if curl -s http://localhost:8108/health | grep '"ok"' > /dev/null; then
53+
echo "TypeSense is ready!"
54+
exit 0
55+
fi
56+
sleep 2
57+
done
58+
echo "TypeSense failed to start"
59+
exit 1
60+
5561
- name: Run tests
5662
run: |
5763
coverage run runtests.py

.github/workflows/lint.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@ jobs:
1515
- name: Checkout
1616
uses: actions/checkout@v2
1717

18-
- name: Setup Python 3.9
18+
- name: Setup Python 3.10
1919
uses: actions/setup-python@v1
2020
with:
21-
python-version: 3.9
21+
python-version: "3.10"
2222

2323
- name: Upgrade Setuptools
2424
run: pip install --upgrade setuptools wheel

.github/workflows/pypi.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ jobs:
2020
- name: Checkout
2121
uses: actions/checkout@v2
2222

23-
- name: Setup Python 3.9
23+
- name: Setup Python 3.10
2424
uses: actions/setup-python@v1
2525
with:
26-
python-version: 3.9
26+
python-version: "3.10"
2727

2828
- name: Upgrade Setuptools
2929
run: pip install --upgrade setuptools wheel

django_typesense/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "0.1.3"
1+
__version__ = "0.1.4"

django_typesense/admin.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from django_typesense.paginator import TypesenseSearchPaginator
1212

1313
logger = logging.getLogger(__name__)
14+
TYPESENSE_MAX_HITS_PER_PAGE = 250
1415

1516

1617
class TypesenseSearchAdminMixin(admin.ModelAdmin):
@@ -124,6 +125,7 @@ def get_typesense_search_results(
124125
"""
125126
if list_per_page is None:
126127
list_per_page = self.list_per_page
128+
list_per_page = min(list_per_page, TYPESENSE_MAX_HITS_PER_PAGE)
127129

128130
results = typesense_search(
129131
collection_name=self.model.collection_class.schema_name,

django_typesense/changelist.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
)
3838

3939
logger = logging.getLogger(__name__)
40+
TYPESENSE_MAX_HITS_PER_PAGE = 250
4041

4142

4243
class ChangeListSearchForm(forms.Form):
@@ -85,10 +86,12 @@ def __init__(
8586
self.date_hierarchy = date_hierarchy
8687
self.search_fields = model_admin.get_typesense_search_fields(request)
8788
self.list_select_related = list_select_related
88-
self.list_per_page = min(list_per_page, 250) # Typesense Max hits per page
89+
self.list_per_page = min(
90+
list_per_page, TYPESENSE_MAX_HITS_PER_PAGE
91+
) # Typesense max hits per page
8992
self.list_max_show_all = min(
90-
list_max_show_all, 250
91-
) # Typesense Max hits per page
93+
list_max_show_all, TYPESENSE_MAX_HITS_PER_PAGE
94+
) # Typesense max hits per page
9295
self.model_admin = model_admin
9396
self.preserved_filters = model_admin.get_preserved_filters(request)
9497
self.sortable_by = sortable_by
@@ -104,6 +107,7 @@ def __init__(
104107
self.page_num = int(request.GET.get(PAGE_VAR, 1))
105108
except ValueError:
106109
self.page_num = 1
110+
self.page_num = max(self.page_num, 1)
107111
self.show_all = ALL_VAR in request.GET
108112
self.is_popup = IS_POPUP_VAR in request.GET
109113
to_field = request.GET.get(TO_FIELD_VAR)
@@ -399,7 +403,7 @@ def get_typesense_results(self, request):
399403
self.page_num,
400404
filter_by=filter_by,
401405
sort_by=sort_by,
402-
list_per_page=self.list_max_show_all # so that if we have all the data if we need to show all
406+
list_per_page=self.list_per_page,
403407
)
404408

405409
# Set query string for clearing all filters.

django_typesense/paginator.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,8 @@ def prepare_results(self):
5050
def page(self, number):
5151
"""Return a Page object for the given 1-based page number."""
5252
number = self.validate_number(number)
53-
bottom = (number - 1) * self.per_page
54-
top = bottom + self.per_page
55-
if top + self.orphans >= self.count:
56-
top = self.count
57-
return self._get_page(self.results[bottom:top], number, self)
53+
# Typesense already returns a paginated response, so avoid slicing again.
54+
return self._get_page(self.results, number, self)
5855

5956
@cached_property
6057
def count(self):

tests/test_typesense_pagination.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
from types import SimpleNamespace
2+
from unittest import mock
3+
4+
from django.test import TestCase
5+
6+
from django_typesense.admin import TypesenseSearchAdminMixin
7+
from django_typesense.paginator import TypesenseSearchPaginator
8+
9+
10+
class DummyCollection:
11+
fields = {"id": None}
12+
13+
def __init__(self, data):
14+
self.validated_data = list(data)
15+
16+
17+
class DummyModel:
18+
_meta = SimpleNamespace(local_fields=[SimpleNamespace(name="id")])
19+
20+
@classmethod
21+
def get_collection_class(cls):
22+
return DummyCollection
23+
24+
@classmethod
25+
def get_collection(cls, data):
26+
return DummyCollection(data)
27+
28+
def __init__(self, **kwargs):
29+
self.__dict__.update(kwargs)
30+
31+
32+
class TestTypesenseSearchPaginator(TestCase):
33+
def test_page_does_not_slice_pre_paginated_results(self):
34+
object_list = {
35+
"found": 500,
36+
"hits": [{"document": {"id": index}} for index in range(250, 500)],
37+
}
38+
paginator = TypesenseSearchPaginator(object_list, per_page=250, model=DummyModel)
39+
40+
page = paginator.page(2)
41+
42+
self.assertEqual(len(page.object_list), 250)
43+
self.assertEqual(page.object_list[0].id, 250)
44+
self.assertEqual(page.object_list[-1].id, 499)
45+
46+
47+
class TestTypesenseSearchAdminPagination(TestCase):
48+
@mock.patch("django_typesense.admin.typesense_search", return_value={"found": 0, "hits": []})
49+
def test_get_typesense_search_results_caps_per_page(self, mocked_typesense_search):
50+
fake_admin = SimpleNamespace(
51+
model=SimpleNamespace(
52+
collection_class=SimpleNamespace(
53+
schema_name="songs",
54+
query_by_fields="title",
55+
)
56+
),
57+
list_per_page=500,
58+
)
59+
60+
TypesenseSearchAdminMixin.get_typesense_search_results(
61+
fake_admin,
62+
request=None,
63+
search_term="song",
64+
page_num=2,
65+
list_per_page=500,
66+
)
67+
68+
mocked_typesense_search.assert_called_once()
69+
_, kwargs = mocked_typesense_search.call_args
70+
self.assertEqual(kwargs["page"], 2)
71+
self.assertEqual(kwargs["per_page"], 250)

0 commit comments

Comments
 (0)