mirror of
https://github.com/ae-utbm/sith.git
synced 2024-11-22 06:03:20 +00:00
commit
bbcc7ffeaa
11
core/api.py
11
core/api.py
@ -2,6 +2,7 @@ from typing import Annotated
|
|||||||
|
|
||||||
import annotated_types
|
import annotated_types
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.db.models import F
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
from ninja import Query
|
from ninja import Query
|
||||||
from ninja_extra import ControllerBase, api_controller, paginate, route
|
from ninja_extra import ControllerBase, api_controller, paginate, route
|
||||||
@ -49,10 +50,16 @@ class UserController(ControllerBase):
|
|||||||
def fetch_profiles(self, pks: Query[set[int]]):
|
def fetch_profiles(self, pks: Query[set[int]]):
|
||||||
return User.objects.filter(pk__in=pks)
|
return User.objects.filter(pk__in=pks)
|
||||||
|
|
||||||
@route.get("/search", response=PaginatedResponseSchema[UserProfileSchema])
|
@route.get(
|
||||||
|
"/search",
|
||||||
|
response=PaginatedResponseSchema[UserProfileSchema],
|
||||||
|
url_name="search_users",
|
||||||
|
)
|
||||||
@paginate(PageNumberPaginationExtra, page_size=20)
|
@paginate(PageNumberPaginationExtra, page_size=20)
|
||||||
def search_users(self, filters: Query[UserFilterSchema]):
|
def search_users(self, filters: Query[UserFilterSchema]):
|
||||||
return filters.filter(User.objects.all())
|
return filters.filter(
|
||||||
|
User.objects.order_by(F("last_login").desc(nulls_last=True))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
DepthValue = Annotated[int, annotated_types.Ge(0), annotated_types.Le(10)]
|
DepthValue = Annotated[int, annotated_types.Ge(0), annotated_types.Le(10)]
|
||||||
|
@ -66,7 +66,6 @@ class UserFilterSchema(FilterSchema):
|
|||||||
SearchQuerySet()
|
SearchQuerySet()
|
||||||
.models(User)
|
.models(User)
|
||||||
.autocomplete(auto=slugify(value).replace("-", " "))
|
.autocomplete(auto=slugify(value).replace("-", " "))
|
||||||
.order_by("-last_update")
|
|
||||||
.values_list("pk", flat=True)
|
.values_list("pk", flat=True)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
85
core/tests/test_user.py
Normal file
85
core/tests/test_user.py
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
|
from django.core.management import call_command
|
||||||
|
from django.test import TestCase
|
||||||
|
from django.urls import reverse
|
||||||
|
from django.utils.timezone import now
|
||||||
|
from model_bakery import baker, seq
|
||||||
|
from model_bakery.recipe import Recipe
|
||||||
|
|
||||||
|
from core.baker_recipes import subscriber_user
|
||||||
|
from core.models import User
|
||||||
|
|
||||||
|
|
||||||
|
class TestSearchUsers(TestCase):
|
||||||
|
@classmethod
|
||||||
|
def setUpTestData(cls):
|
||||||
|
User.objects.all().delete()
|
||||||
|
user_recipe = Recipe(
|
||||||
|
User,
|
||||||
|
first_name=seq("First", suffix="Name"),
|
||||||
|
last_name=seq("Last", suffix="Name"),
|
||||||
|
nick_name=seq("Nick", suffix="Name"),
|
||||||
|
)
|
||||||
|
cls.users = [
|
||||||
|
user_recipe.make(last_login=None),
|
||||||
|
*user_recipe.make(
|
||||||
|
last_login=seq(now() - timedelta(days=30), timedelta(days=1)),
|
||||||
|
_quantity=10,
|
||||||
|
_bulk_create=True,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
call_command("update_index", "core", "--remove")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def tearDownClass(cls):
|
||||||
|
super().tearDownClass()
|
||||||
|
# restore the index
|
||||||
|
call_command("update_index", "core", "--remove")
|
||||||
|
|
||||||
|
def test_order(self):
|
||||||
|
"""Test that users are ordered by last login date."""
|
||||||
|
self.client.force_login(subscriber_user.make())
|
||||||
|
|
||||||
|
response = self.client.get(reverse("api:search_users") + "?search=First")
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.json()["count"] == 11
|
||||||
|
# The users are ordered by last login date, so we need to reverse the list
|
||||||
|
assert [r["id"] for r in response.json()["results"]] == [
|
||||||
|
u.id for u in self.users[::-1]
|
||||||
|
]
|
||||||
|
|
||||||
|
def test_search_case_insensitive(self):
|
||||||
|
"""Test that the search is case insensitive."""
|
||||||
|
self.client.force_login(subscriber_user.make())
|
||||||
|
|
||||||
|
expected = [u.id for u in self.users[::-1]]
|
||||||
|
for term in ["first", "First", "FIRST"]:
|
||||||
|
response = self.client.get(reverse("api:search_users") + f"?search={term}")
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.json()["count"] == 11
|
||||||
|
assert [r["id"] for r in response.json()["results"]] == expected
|
||||||
|
|
||||||
|
def test_search_nick_name(self):
|
||||||
|
"""Test that the search can be done on the nick name."""
|
||||||
|
self.client.force_login(subscriber_user.make())
|
||||||
|
|
||||||
|
# this should return users with nicknames Nick11, Nick10 and Nick1
|
||||||
|
response = self.client.get(reverse("api:search_users") + "?search=Nick1")
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert [r["id"] for r in response.json()["results"]] == [
|
||||||
|
self.users[10].id,
|
||||||
|
self.users[9].id,
|
||||||
|
self.users[0].id,
|
||||||
|
]
|
||||||
|
|
||||||
|
def test_search_special_characters(self):
|
||||||
|
"""Test that the search can be done on special characters."""
|
||||||
|
belix = baker.make(User, nick_name="Bélix")
|
||||||
|
call_command("update_index", "core")
|
||||||
|
self.client.force_login(subscriber_user.make())
|
||||||
|
|
||||||
|
# this should return users with first names First1 and First10
|
||||||
|
response = self.client.get(reverse("api:search_users") + "?search=bél")
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert [r["id"] for r in response.json()["results"]] == [belix.id]
|
@ -85,7 +85,7 @@ def search_user(query):
|
|||||||
SearchQuerySet()
|
SearchQuerySet()
|
||||||
.models(User)
|
.models(User)
|
||||||
.autocomplete(auto=query)
|
.autocomplete(auto=query)
|
||||||
.order_by("-last_update")[:20]
|
.order_by("-last_login")[:20]
|
||||||
)
|
)
|
||||||
return [r.object for r in res]
|
return [r.object for r in res]
|
||||||
except TypeError:
|
except TypeError:
|
||||||
|
Loading…
Reference in New Issue
Block a user