diff --git a/api/src/data_inclusion/api/inclusion_data/routes.py b/api/src/data_inclusion/api/inclusion_data/routes.py index 71e57039..85f1b6b7 100644 --- a/api/src/data_inclusion/api/inclusion_data/routes.py +++ b/api/src/data_inclusion/api/inclusion_data/routes.py @@ -225,6 +225,14 @@ def list_services_endpoint( Chaque résultat renvoyé a (au moins) une typologie dans cette liste.""" ), ] = None, + score_qualite_minimum: Annotated[ + Optional[float], + fastapi.Query( + description="""[BETA] Score de qualité minimum. + Les résultats renvoyés ont un score de qualité supérieur ou égal à ce + score.""" + ), + ] = None, inclure_suspendus: Annotated[ Optional[bool], fastapi.Query( @@ -251,6 +259,7 @@ def list_services_endpoint( profils=profils, modes_accueil=modes_accueil, types=types, + score_qualite_minimum=score_qualite_minimum, include_outdated=inclure_suspendus, ) background_tasks.add_task( @@ -384,6 +393,14 @@ def search_services_endpoint( Chaque résultat renvoyé a (au moins) une typologie dans cette liste.""" ), ] = None, + score_qualite_minimum: Annotated[ + Optional[float], + fastapi.Query( + description="""Score de qualité minimum. + Les résultats renvoyés ont un score de qualité supérieur ou égal à ce + score.""" + ), + ] = None, inclure_suspendus: Annotated[ Optional[bool], fastapi.Query( @@ -445,6 +462,7 @@ def search_services_endpoint( profils=profils, types=types, search_point=search_point, + score_qualite_minimum=score_qualite_minimum, include_outdated=inclure_suspendus, ) diff --git a/api/src/data_inclusion/api/inclusion_data/services.py b/api/src/data_inclusion/api/inclusion_data/services.py index a659cf4a..4ed3fb9e 100644 --- a/api/src/data_inclusion/api/inclusion_data/services.py +++ b/api/src/data_inclusion/api/inclusion_data/services.py @@ -153,6 +153,13 @@ def filter_services_by_types( ) +def filter_services_by_score_qualite( + query: sqla.Select, + score_qualite_minimum: float, +): + return query.filter(models.Service.score_qualite >= score_qualite_minimum) + + def filter_outdated_services( query: sqla.Select, ): @@ -265,6 +272,7 @@ def filter_services( profils: list[di_schema.Profil] | None = None, modes_accueil: list[di_schema.ModeAccueil] | None = None, types: list[di_schema.TypologieService] | None = None, + score_qualite_minimum: float | None = None, include_outdated: bool | None = False, ) -> sqla.Select: """Common filters for services.""" @@ -287,6 +295,9 @@ def filter_services( if types is not None: query = filter_services_by_types(query, types) + if score_qualite_minimum is not None: + query = filter_services_by_score_qualite(query, score_qualite_minimum) + if not include_outdated: query = filter_outdated_services(query) @@ -305,6 +316,7 @@ def list_services( profils: list[di_schema.Profil] | None = None, modes_accueil: list[di_schema.ModeAccueil] | None = None, types: list[di_schema.TypologieService] | None = None, + score_qualite_minimum: float | None = None, include_outdated: bool | None = False, ): query = ( @@ -332,6 +344,7 @@ def list_services( profils=profils, modes_accueil=modes_accueil, types=types, + score_qualite_minimum=score_qualite_minimum, include_outdated=include_outdated, ) @@ -354,6 +367,7 @@ def search_services( profils: list[di_schema.Profil] | None = None, types: list[di_schema.TypologieService] | None = None, search_point: str | None = None, + score_qualite_minimum: float | None = None, include_outdated: bool | None = False, ): query = ( @@ -454,6 +468,7 @@ def search_services( profils=profils, modes_accueil=modes_accueil, types=types, + score_qualite_minimum=score_qualite_minimum, include_outdated=include_outdated, ) diff --git a/api/tests/e2e/api/__snapshots__/test_inclusion_data.ambr b/api/tests/e2e/api/__snapshots__/test_inclusion_data.ambr index 2ad666ba..60f52d9b 100644 --- a/api/tests/e2e/api/__snapshots__/test_inclusion_data.ambr +++ b/api/tests/e2e/api/__snapshots__/test_inclusion_data.ambr @@ -412,6 +412,17 @@ }, "description": "Une liste de typologies de service.\n Chaque résultat renvoyé a (au moins) une typologie dans cette liste." }, + { + "name": "score_qualite_minimum", + "in": "query", + "required": false, + "schema": { + "type": "number", + "description": "Score de qualité minimum.\n Les résultats renvoyés ont un score de qualité supérieur ou égal à ce\n score.", + "title": "Score Qualite Minimum" + }, + "description": "Score de qualité minimum.\n Les résultats renvoyés ont un score de qualité supérieur ou égal à ce\n score." + }, { "name": "inclure_suspendus", "in": "query", @@ -666,6 +677,17 @@ }, "description": "Une liste de typologies de service.\n Chaque résultat renvoyé a (au moins) une typologie dans cette liste." }, + { + "name": "score_qualite_minimum", + "in": "query", + "required": false, + "schema": { + "type": "number", + "description": "Score de qualité minimum.\n Les résultats renvoyés ont un score de qualité supérieur ou égal à ce\n score.", + "title": "Score Qualite Minimum" + }, + "description": "Score de qualité minimum.\n Les résultats renvoyés ont un score de qualité supérieur ou égal à ce\n score." + }, { "name": "inclure_suspendus", "in": "query", diff --git a/api/tests/e2e/api/test_inclusion_data.py b/api/tests/e2e/api/test_inclusion_data.py index 63cb1036..38993228 100644 --- a/api/tests/e2e/api/test_inclusion_data.py +++ b/api/tests/e2e/api/test_inclusion_data.py @@ -663,6 +663,27 @@ def test_list_services_by_types(api_client, url): assert_paginated_response_data(response.json(), total=0) +@pytest.mark.with_token +@pytest.mark.parametrize("url", ["/api/v0/services", "/api/v0/search/services"]) +def test_list_services_by_score_qualite(api_client, url): + service_1 = factories.ServiceFactory(score_qualite=0.5) + service_2 = factories.ServiceFactory(score_qualite=0.7) + factories.ServiceFactory(score_qualite=0.2) + + response = api_client.get( + url, + params={"score_qualite_minimum": service_1.score_qualite}, + ) + + assert response.status_code == 200 + resp_data = response.json() + assert_paginated_response_data(resp_data, total=2) + assert {d["id"] for d in list_resources_data(resp_data)} == { + service_1.id, + service_2.id, + } + + @pytest.mark.with_token @pytest.mark.parametrize("url", ["/api/v0/services", "/api/v0/search/services"]) def test_can_filter_services_by_frais(api_client, url):