mirror of
				https://github.com/ae-utbm/sith.git
				synced 2025-11-04 11:03:04 +00:00 
			
		
		
		
	Use full text search in pedagogy uv search api
This commit is contained in:
		@@ -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.
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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)
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user