Skip to content

Commit 0049de3

Browse files
authored
Merge pull request #1687 from wger-project/feature/language-filter
Allow deactivating the language filter when searching for ingredients and exercises
2 parents 06e9b99 + f49f81b commit 0049de3

File tree

7 files changed

+154
-16
lines changed

7 files changed

+154
-16
lines changed

wger/exercises/api/views.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@
8787
HTML_ATTRIBUTES_WHITELIST,
8888
HTML_STYLES_WHITELIST,
8989
HTML_TAG_WHITELIST,
90+
SEARCH_ALL_LANGUAGES,
9091
)
9192
from wger.utils.db import is_postgres_db
9293
from wger.utils.language import load_language
@@ -346,8 +347,14 @@ def search(request):
346347
if not q:
347348
return Response(response)
348349

350+
# Filter the appropriate languages
349351
languages = [load_language(l) for l in language_codes.split(',')]
350-
query = Exercise.objects.filter(language__in=languages).only('name')
352+
if language_codes == SEARCH_ALL_LANGUAGES:
353+
query = Exercise.objects.all()
354+
else:
355+
query = Exercise.objects.filter(language__in=languages)
356+
357+
query = query.only('name')
351358

352359
# Postgres uses a full-text search
353360
if is_postgres_db():

wger/exercises/tests/test_search_api.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,3 +94,12 @@ def test_search_several_language_codes(self):
9494

9595
self.assertEqual(response.status_code, status.HTTP_200_OK)
9696
self.assertEqual(len(response.data['suggestions']), 4)
97+
98+
def test_search_all_languages(self):
99+
"""
100+
Passing different language codes works correctly
101+
"""
102+
response = self.client.get(self.url + '?term=demo&language=*')
103+
104+
self.assertEqual(response.status_code, status.HTTP_200_OK)
105+
self.assertEqual(len(response.data['suggestions']), 4)

wger/nutrition/api/views.py

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
from django.contrib.postgres.search import TrigramSimilarity
2424
from django.utils.decorators import method_decorator
2525
from django.views.decorators.cache import cache_page
26-
2726
# Third Party
2827
from drf_spectacular.types import OpenApiTypes
2928
from drf_spectacular.utils import (
@@ -73,12 +72,14 @@
7372
NutritionPlan,
7473
WeightUnit,
7574
)
76-
from wger.utils.constants import ENGLISH_SHORT_NAME
75+
from wger.utils.constants import (
76+
ENGLISH_SHORT_NAME,
77+
SEARCH_ALL_LANGUAGES,
78+
)
7779
from wger.utils.db import is_postgres_db
7880
from wger.utils.language import load_language
7981
from wger.utils.viewsets import WgerOwnerObjectModelViewSet
8082

81-
8283
logger = logging.getLogger(__name__)
8384

8485

@@ -211,15 +212,21 @@ def search(request):
211212
term = request.GET.get('term', None)
212213
language_codes = request.GET.get('language', ENGLISH_SHORT_NAME)
213214
results = []
214-
json_response = {}
215+
response = {}
215216

216217
if not term:
217-
return Response(json_response)
218+
return Response(response)
218219

220+
query = Ingredient.objects.all()
221+
222+
# Filter the appropriate languages
219223
languages = [load_language(l) for l in language_codes.split(',')]
220-
query = Ingredient.objects.filter(
221-
language__in=languages,
222-
).only('name')
224+
if language_codes != SEARCH_ALL_LANGUAGES:
225+
query = query.filter(
226+
language__in=languages,
227+
)
228+
229+
query = query.only('name')
223230

224231
# Postgres uses a full-text search
225232
if is_postgres_db():
@@ -252,9 +259,9 @@ def search(request):
252259
},
253260
}
254261
results.append(ingredient_json)
255-
json_response['suggestions'] = results
262+
response['suggestions'] = results
256263

257-
return Response(json_response)
264+
return Response(response)
258265

259266

260267
class ImageViewSet(viewsets.ReadOnlyModelViewSet):

wger/nutrition/fixtures/test-ingredients.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -143,8 +143,8 @@
143143
"license_author": "wger test",
144144
"last_update": "2013-02-12T01:00:00+01:00",
145145
"created": "2013-04-05T01:00:00+01:00",
146-
"name": "Raw ingredient",
147-
"language": 2,
146+
"name": "Testzutat 123",
147+
"language": 1,
148148
"sodium": "2.1",
149149
"energy": 180,
150150
"fat": "2",
@@ -333,7 +333,7 @@
333333
"last_update": "2013-02-12T01:00:00+01:00",
334334
"created": "2013-04-05T01:00:00+01:00",
335335
"name": "Needed for guest user",
336-
"language": 2,
336+
"language": 3,
337337
"sodium": "2.1",
338338
"energy": 180,
339339
"fat": "2",
@@ -360,7 +360,7 @@
360360
"last_update": "2013-02-12T01:00:00+01:00",
361361
"created": "2013-04-05T01:00:00+01:00",
362362
"name": "Needed for guest user",
363-
"language": 2,
363+
"language": 3,
364364
"sodium": "2.1",
365365
"energy": 180,
366366
"fat": "2",

wger/nutrition/tests/test_ingredient_overview.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ def test_overview(self):
5656
self.assertEqual(response.status_code, 200)
5757
self.assertEqual(len(response.context['ingredients_list']), PAGINATION_OBJECTS_PER_PAGE)
5858

59-
rest_ingredients = 14
59+
rest_ingredients = 11
6060
response = self.client.get(reverse('nutrition:ingredient:list'), {'page': 3})
6161
self.assertEqual(response.status_code, 200)
6262
self.assertEqual(len(response.context['ingredients_list']), rest_ingredients)
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
# This file is part of wger Workout Manager.
2+
#
3+
# wger Workout Manager is free software: you can redistribute it and/or modify
4+
# it under the terms of the GNU Affero General Public License as published by
5+
# the Free Software Foundation, either version 3 of the License, or
6+
# (at your option) any later version.
7+
#
8+
# wger Workout Manager is distributed in the hope that it will be useful,
9+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+
# GNU General Public License for more details.
12+
#
13+
# You should have received a copy of the GNU Affero General Public License
14+
# along with Workout Manager. If not, see <http://www.gnu.org/licenses/>.
15+
16+
# Third Party
17+
from rest_framework import status
18+
19+
# wger
20+
from wger.core.tests.api_base_test import ApiBaseTestCase
21+
from wger.core.tests.base_testcase import BaseTestCase
22+
23+
24+
class SearchIngredientApiTestCase(BaseTestCase, ApiBaseTestCase):
25+
url = '/api/v2/ingredient/search/'
26+
27+
def setUp(self):
28+
super().setUp()
29+
self.init_media_root()
30+
31+
def test_basic_search_logged_out(self):
32+
"""
33+
Logged-out users are also allowed to use the search
34+
"""
35+
response = self.client.get(self.url + '?term=test')
36+
result1 = response.data['suggestions'][0]
37+
38+
self.assertEqual(response.status_code, status.HTTP_200_OK)
39+
self.assertEqual(len(response.data['suggestions']), 2)
40+
self.assertEqual(result1['value'], 'Ingredient, test, 2, organic, raw')
41+
self.assertEqual(result1['data']['id'], 2)
42+
43+
def test_basic_search_logged_in(self):
44+
"""
45+
Logged-in users get the same results
46+
"""
47+
self.authenticate('test')
48+
response = self.client.get(self.url + '?term=test')
49+
result1 = response.data['suggestions'][0]
50+
51+
self.assertEqual(response.status_code, status.HTTP_200_OK)
52+
self.assertEqual(len(response.data['suggestions']), 2)
53+
self.assertEqual(result1['value'], 'Ingredient, test, 2, organic, raw')
54+
self.assertEqual(result1['data']['id'], 2)
55+
56+
def test_search_language_code_en(self):
57+
"""
58+
Explicitly passing the en language code (same as no code)
59+
"""
60+
response = self.client.get(self.url + '?term=test&language=en')
61+
result1 = response.data['suggestions'][0]
62+
63+
self.assertEqual(response.status_code, status.HTTP_200_OK)
64+
self.assertEqual(len(response.data['suggestions']), 2)
65+
self.assertEqual(result1['value'], 'Ingredient, test, 2, organic, raw')
66+
self.assertEqual(result1['data']['id'], 2)
67+
68+
def test_search_language_code_en_no_results(self):
69+
"""
70+
The "Testzutat" ingredient should not be found when searching in English
71+
"""
72+
response = self.client.get(self.url + '?term=Testzutat&language=en')
73+
74+
self.assertEqual(response.status_code, status.HTTP_200_OK)
75+
self.assertEqual(len(response.data['suggestions']), 0)
76+
77+
def test_search_language_code_de(self):
78+
"""
79+
The "Testübung" exercise should be only found when searching in German
80+
"""
81+
response = self.client.get(self.url + '?term=Testzutat&language=de')
82+
result1 = response.data['suggestions'][0]
83+
84+
self.assertEqual(response.status_code, status.HTTP_200_OK)
85+
self.assertEqual(len(response.data['suggestions']), 1)
86+
self.assertEqual(result1['value'], 'Testzutat 123')
87+
self.assertEqual(result1['data']['id'], 6)
88+
89+
def test_search_several_language_codes(self):
90+
"""
91+
Passing different language codes works correctly
92+
"""
93+
response = self.client.get(self.url + '?term=guest&language=en,de')
94+
95+
self.assertEqual(response.status_code, status.HTTP_200_OK)
96+
self.assertEqual(len(response.data['suggestions']), 5)
97+
98+
def test_search_unknown_language_codes(self):
99+
"""
100+
Unknown language codes are ignored
101+
"""
102+
response = self.client.get(self.url + '?term=guest&language=en,de,kg')
103+
104+
self.assertEqual(response.status_code, status.HTTP_200_OK)
105+
self.assertEqual(len(response.data['suggestions']), 5)
106+
107+
def test_search_all_languages(self):
108+
"""
109+
Disable all language filters
110+
"""
111+
response = self.client.get(self.url + '?term=guest&language=*')
112+
113+
self.assertEqual(response.status_code, status.HTTP_200_OK)
114+
self.assertEqual(len(response.data['suggestions']), 7)

wger/utils/constants.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,4 @@
6969

7070
# API
7171
API_MAX_ITEMS = 999
72+
SEARCH_ALL_LANGUAGES = '*'

0 commit comments

Comments
 (0)