Use full text search in pedagogy uv search api

This commit is contained in:
Antoine Bartuccio 2024-07-29 10:23:30 +02:00
parent 39151b61e7
commit 1bd887567e
2 changed files with 59 additions and 10 deletions

View File

@ -1,6 +1,8 @@
from typing import Literal
from django.db.models import Q
from django.utils import html
from haystack.query import SearchQuerySet
from ninja import FilterSchema, ModelSchema, Schema
from pydantic import AliasPath, ConfigDict, Field, TypeAdapter
from pydantic.alias_generators import to_camel
@ -120,6 +122,27 @@ class UvFilterSchema(FilterSchema):
language: str = "FR"
department: set[str] | None = Field(None, q="department__in")
def filter_search(self, value: str | None) -> Q:
"""Special filter for the search text.
It does a full text search if available.
"""
if not value:
return Q()
if len(value) < 3 or (len(value) < 5 and any(c.isdigit() for c in value)):
# Likely to be an UV code
return Q(code__istartswith=value)
qs = list(
SearchQuerySet()
.models(UV)
.autocomplete(auto=html.escape(value))
.values_list("pk", flat=True)
)
return Q(id__in=qs)
def filter_semester(self, value: set[str] | None) -> Q:
"""Special filter for the semester.

View File

@ -2,6 +2,7 @@ import json
from django.conf import settings
from django.test import TestCase
from django.test.testcases import call_command
from django.urls import reverse
from model_bakery import baker
from model_bakery.recipe import Recipe
@ -21,16 +22,31 @@ class TestUVSearch(TestCase):
uv_recipe = Recipe(UV, author=cls.root)
uvs = [
uv_recipe.prepare(
code="AP4A", credit_type="CS", semester="AUTUMN", department="GI"
code="AP4A",
credit_type="CS",
semester="AUTUMN",
department="GI",
manager="francky",
title="Programmation Orientée Objet: Concepts fondamentaux et mise en pratique avec le langage C++",
),
uv_recipe.prepare(
code="MT01", credit_type="CS", semester="AUTUMN", department="TC"
code="MT01",
credit_type="CS",
semester="AUTUMN",
department="TC",
manager="ben",
title="Intégration1. Algèbre linéaire - Fonctions de deux variables",
),
uv_recipe.prepare(
code="PHYS11", credit_type="CS", semester="AUTUMN", department="TC"
),
uv_recipe.prepare(
code="TNEV", credit_type="TM", semester="SPRING", department="TC"
code="TNEV",
credit_type="TM",
semester="SPRING",
department="TC",
manager="moss",
title="tnetennba",
),
uv_recipe.prepare(
code="MT10", credit_type="TM", semester="AUTUMN", department="IMSI"
@ -40,9 +56,11 @@ class TestUVSearch(TestCase):
credit_type="TM",
semester="AUTUMN_AND_SPRING",
department="GI",
manager="francky",
),
]
UV.objects.bulk_create(uvs)
call_command("update_index")
def test_permissions(self):
# Test with anonymous user
@ -92,14 +110,22 @@ class TestUVSearch(TestCase):
],
}
def test_search_by_code(self):
def test_search_by_text(self):
self.client.force_login(self.root)
res = self.client.get(self.url + "?search=MT")
assert res.status_code == 200
assert {uv["code"] for uv in json.loads(res.content)["results"]} == {
"MT01",
"MT10",
}
for query, expected in (
# UV code search case insensitive
("m", {"MT01", "MT10"}),
("M", {"MT01", "MT10"}),
("mt", {"MT01", "MT10"}),
("MT", {"MT01", "MT10"}),
("algèbre", {"MT01"}), # Title search case insensitive
# Manager search
("moss", {"TNEV"}),
("francky", {"DA50", "AP4A"}),
):
res = self.client.get(self.url + f"?search={query}")
assert res.status_code == 200
assert {uv["code"] for uv in json.loads(res.content)["results"]} == expected
def test_search_by_credit_type(self):
self.client.force_login(self.root)