From fd59cd29385dd470181e3b9ce507cc9d2a08e91a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Gounot?= Date: Thu, 31 Oct 2024 16:38:48 +0100 Subject: [PATCH 01/38] =?UTF-8?q?Ajout=20d'un=20mod=C3=A8le=20FundingLabel?= =?UTF-8?q?=20repr=C3=A9sentant=20un=20label=20de=20financement?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- back/dora/services/models.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/back/dora/services/models.py b/back/dora/services/models.py index b864c7379..444775b6c 100644 --- a/back/dora/services/models.py +++ b/back/dora/services/models.py @@ -122,6 +122,12 @@ class Meta: verbose_name_plural = "Types de service" +class FundingLabel(EnumModel): + class Meta: + verbose_name = "Label de financement" + verbose_name_plural = "Labels de financement" + + class BeneficiaryAccessMode(EnumModel): class Meta: verbose_name = "Mode d'orientation bénéficiaire" From 355655ff22b6643b5b7fe337fbd00a0ba8a47673 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Gounot?= Date: Thu, 31 Oct 2024 17:17:05 +0100 Subject: [PATCH 02/38] =?UTF-8?q?Ajout=20migration=20de=20cr=C3=A9ation=20?= =?UTF-8?q?du=20mod=C3=A8le=20FundingLabel?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...111_fundinglabel_service_funding_labels.py | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 back/dora/services/migrations/0111_fundinglabel_service_funding_labels.py diff --git a/back/dora/services/migrations/0111_fundinglabel_service_funding_labels.py b/back/dora/services/migrations/0111_fundinglabel_service_funding_labels.py new file mode 100644 index 000000000..239dcaa53 --- /dev/null +++ b/back/dora/services/migrations/0111_fundinglabel_service_funding_labels.py @@ -0,0 +1,41 @@ +# Generated by Django 4.2.16 on 2024-10-31 16:16 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("services", "0110_remove_service_fee_pass_numerique"), + ] + + operations = [ + migrations.CreateModel( + name="FundingLabel", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("value", models.CharField(db_index=True, max_length=255, unique=True)), + ("label", models.CharField(max_length=255)), + ], + options={ + "verbose_name": "Label de financement", + "verbose_name_plural": "Labels de financement", + }, + ), + migrations.AddField( + model_name="service", + name="funding_labels", + field=models.ManyToManyField( + blank=True, + to="services.fundinglabel", + verbose_name="Labels de financement", + ), + ), + ] From 4dd77413dc84db28fa947441301e77928f399be0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Gounot?= Date: Thu, 31 Oct 2024 17:15:39 +0100 Subject: [PATCH 03/38] Ajout d'une page admin pour les labels de financement --- back/dora/services/admin.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/back/dora/services/admin.py b/back/dora/services/admin.py index 366b0a23e..397f11e1f 100644 --- a/back/dora/services/admin.py +++ b/back/dora/services/admin.py @@ -10,6 +10,7 @@ CoachOrientationMode, ConcernedPublic, Credential, + FundingLabel, LocationKind, Requirement, SavedSearch, @@ -208,3 +209,4 @@ class SavedSearchAdmin(admin.ModelAdmin): admin.site.register(ServiceKind, EnumAdmin) admin.site.register(ServiceSubCategory, EnumAdmin) admin.site.register(ServiceSource, EnumAdmin) +admin.site.register(FundingLabel, EnumAdmin) From f0d9c9cd2ac65c62f886b5e8efe190bb62641c6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Gounot?= Date: Thu, 31 Oct 2024 17:16:04 +0100 Subject: [PATCH 04/38] =?UTF-8?q?Ajout=20d'un=20champ=20funding=5Flabels?= =?UTF-8?q?=20au=20mod=C3=A8le=20Service?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- back/dora/services/models.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/back/dora/services/models.py b/back/dora/services/models.py index 444775b6c..6b79bf458 100644 --- a/back/dora/services/models.py +++ b/back/dora/services/models.py @@ -252,6 +252,12 @@ class Service(ModerationMixin, models.Model): blank=True, ) + funding_labels = models.ManyToManyField( + FundingLabel, + verbose_name="Labels de financement", + blank=True, + ) + ############ # Conditions From 03ef0bf4ef047ebed40613b23e1d7cff50529aef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Gounot?= Date: Thu, 31 Oct 2024 17:33:45 +0100 Subject: [PATCH 05/38] ServiceSerializer: ajout des labels de financement --- back/dora/services/serializers.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/back/dora/services/serializers.py b/back/dora/services/serializers.py index ee3d2ac78..893477a6f 100644 --- a/back/dora/services/serializers.py +++ b/back/dora/services/serializers.py @@ -22,6 +22,7 @@ CoachOrientationMode, ConcernedPublic, Credential, + FundingLabel, LocationKind, Requirement, SavedSearch, @@ -166,6 +167,15 @@ class ServiceSerializer(serializers.ModelSerializer): subcategories_display = serializers.SlugRelatedField( source="subcategories", slug_field="label", many=True, read_only=True ) + funding_labels = serializers.SlugRelatedField( + slug_field="value", + queryset=FundingLabel.objects.all(), + many=True, + required=False, + ) + funding_labels_display = serializers.SlugRelatedField( + source="funding_labels", slug_field="label", many=True, read_only=True + ) access_conditions = CreatablePrimaryKeyRelatedField( many=True, queryset=AccessCondition.objects.all(), @@ -290,6 +300,8 @@ class Meta: "forms", "forms_info", "full_desc", + "funding_labels", + "funding_labels_display", "geom", "has_already_been_unpublished", "is_available", From 20effa3db7da6df035f36bff6baa5e2ed93ac530 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Gounot?= Date: Thu, 31 Oct 2024 17:48:07 +0100 Subject: [PATCH 06/38] map_service(): ajout des champs labels de financement --- back/dora/data_inclusion/mappings.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/back/dora/data_inclusion/mappings.py b/back/dora/data_inclusion/mappings.py index 1d6e943a3..7f266fc2c 100644 --- a/back/dora/data_inclusion/mappings.py +++ b/back/dora/data_inclusion/mappings.py @@ -257,6 +257,8 @@ def map_service(service_data: dict, is_authenticated: bool) -> dict: "forms": None, "forms_info": None, "full_desc": service_data["presentation_detail"] or "", + "funding_labels": [], + "funding_labels_display": [], "geom": None, "has_already_been_unpublished": None, "is_available": True, From 24418374982050361c435660b5182f1a99de4a5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Gounot?= Date: Mon, 4 Nov 2024 18:37:12 +0100 Subject: [PATCH 07/38] map_search_result(): ajout du champ de labels de financement --- back/dora/data_inclusion/mappings.py | 1 + 1 file changed, 1 insertion(+) diff --git a/back/dora/data_inclusion/mappings.py b/back/dora/data_inclusion/mappings.py index 7f266fc2c..20565b83d 100644 --- a/back/dora/data_inclusion/mappings.py +++ b/back/dora/data_inclusion/mappings.py @@ -78,6 +78,7 @@ def map_search_result(result: dict, supported_service_kinds: list[str]) -> dict: "location_kinds": location_kinds, "kinds": kinds, "fee_condition": service_data["frais"][0] if service_data["frais"] else None, + "funding_labels": [], "modification_date": service_data["date_maj"], "name": service_data["nom"], "short_desc": service_data["presentation_resume"] or "", From bc4845e6b0735fece8b67aba8b46203ee446370b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Gounot?= Date: Mon, 4 Nov 2024 14:53:24 +0100 Subject: [PATCH 08/38] =?UTF-8?q?search=5Fservices():=20retourne=20un=20di?= =?UTF-8?q?citonnaire=20de=20m=C3=A9tadonn=C3=A9es=20en=20plus=20des=20r?= =?UTF-8?q?=C3=A9sultats?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- back/dora/services/search.py | 13 ++++++++----- back/dora/services/views.py | 2 +- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/back/dora/services/search.py b/back/dora/services/search.py index aa4f17628..51055d257 100644 --- a/back/dora/services/search.py +++ b/back/dora/services/search.py @@ -313,7 +313,9 @@ def _get_dora_results( with_onsite, ) - return SearchResultSerializer(results, many=True, context={"request": request}).data + return SearchResultSerializer( + results, many=True, context={"request": request} + ).data, {} def search_services( @@ -328,7 +330,7 @@ def search_services( di_client: Optional[data_inclusion.DataInclusionClient] = None, lat: Optional[float] = None, lon: Optional[float] = None, -) -> list[dict]: +) -> (list[dict], dict): """Search services from all available repositories. It always includes results from dora own databases. @@ -339,7 +341,8 @@ def search_services( Note : this is the only point where di_client is "injected" Returns: - A list of search results by SearchResultSerializer. + - A list of search results by SearchResultSerializer. + - A metadata dictionary """ di_results = ( _get_di_results( @@ -357,7 +360,7 @@ def search_services( else [] ) - dora_results = _get_dora_results( + dora_results, metadata = _get_dora_results( request=request, categories=categories, subcategories=subcategories, @@ -371,4 +374,4 @@ def search_services( ) all_results = [*dora_results, *di_results] - return _sort_services(all_results) + return _sort_services(all_results), metadata diff --git a/back/dora/services/views.py b/back/dora/services/views.py index c1057bab3..2912a3bdb 100644 --- a/back/dora/services/views.py +++ b/back/dora/services/views.py @@ -790,7 +790,7 @@ def search(request): di_client = data_inclusion.di_client_factory() - sorted_services = search_services( + sorted_services, metadata = search_services( request=request, di_client=di_client, city_code=city_code, From e2fe45184c0ea42a682175a4c1e6317b7a3ccfda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Gounot?= Date: Mon, 4 Nov 2024 15:20:51 +0100 Subject: [PATCH 09/38] Ajout d'un FundingLabelSerializer --- back/dora/services/serializers.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/back/dora/services/serializers.py b/back/dora/services/serializers.py index 893477a6f..3471da56d 100644 --- a/back/dora/services/serializers.py +++ b/back/dora/services/serializers.py @@ -820,3 +820,12 @@ def get_distance(self, obj): def get_coordinates(self, obj): if obj.geom: return (obj.geom.x, obj.geom.y) + + +class FundingLabelSerializer(serializers.ModelSerializer): + class Meta: + model = FundingLabel + fields = [ + "value", + "label", + ] From 25473c7ad87463e8fdfd31c811928258111ab864 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Gounot?= Date: Mon, 4 Nov 2024 15:22:11 +0100 Subject: [PATCH 10/38] =?UTF-8?q?=5Fget=5Fdora=5Fresults():=20retourne=20l?= =?UTF-8?q?es=20labels=20de=20financement=20des=20r=C3=A9sultats?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- back/dora/services/search.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/back/dora/services/search.py b/back/dora/services/search.py index 51055d257..294a08a11 100644 --- a/back/dora/services/search.py +++ b/back/dora/services/search.py @@ -19,7 +19,8 @@ from dora.structures.models import Structure from .constants import EXCLUDED_DI_SERVICES_THEMATIQUES -from .serializers import SearchResultSerializer +from .models import FundingLabel +from .serializers import FundingLabelSerializer, SearchResultSerializer from .utils import filter_services_by_city_code MAX_DISTANCE = 50 @@ -313,9 +314,11 @@ def _get_dora_results( with_onsite, ) + funding_labels = FundingLabel.objects.filter(service__in=results).distinct() + return SearchResultSerializer( results, many=True, context={"request": request} - ).data, {} + ).data, {"funding_labels": FundingLabelSerializer(funding_labels, many=True).data} def search_services( From 739fc87da7ebb919610aeed61e54bee1d88426a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Gounot?= Date: Mon, 4 Nov 2024 15:22:32 +0100 Subject: [PATCH 11/38] =?UTF-8?q?Vue=20search():=20retourne=20les=20labels?= =?UTF-8?q?=20de=20financement=20des=20r=C3=A9sultats?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- back/dora/services/views.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/back/dora/services/views.py b/back/dora/services/views.py index 2912a3bdb..c8526d0ec 100644 --- a/back/dora/services/views.py +++ b/back/dora/services/views.py @@ -804,7 +804,13 @@ def search(request): lon=lon, ) - return Response({"city_bounds": city.geom.extent, "services": sorted_services}) + return Response( + { + "city_bounds": city.geom.extent, + "funding_labels": metadata["funding_labels"], + "services": sorted_services, + } + ) def share_service(request, service, is_di): From 2530dcd4163718b91a2ef0d2bfe297f165408984 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Gounot?= Date: Mon, 4 Nov 2024 15:27:42 +0100 Subject: [PATCH 12/38] =?UTF-8?q?SearchResultSerializer:=20trie=20les=20ch?= =?UTF-8?q?amps=20dans=20l'ordre=20alphab=C3=A9tiqur?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- back/dora/services/serializers.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/back/dora/services/serializers.py b/back/dora/services/serializers.py index 3471da56d..62d890e93 100644 --- a/back/dora/services/serializers.py +++ b/back/dora/services/serializers.py @@ -793,13 +793,16 @@ class Meta: fields = [ "address1", "address2", + "beneficiaries_access_modes", "city", + "coach_orientation_modes", "coordinates", "diffusion_zone_type", "distance", + "fee_condition", + "is_orientable", "kinds", "location_kinds", - "fee_condition", "modification_date", "name", "postal_code", @@ -807,11 +810,8 @@ class Meta: "short_desc", "slug", "status", - "structure_info", "structure", - "is_orientable", - "coach_orientation_modes", - "beneficiaries_access_modes", + "structure_info", ] def get_distance(self, obj): From b3fc66145643323aa93329f69c892e9b25d7ed8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Gounot?= Date: Mon, 4 Nov 2024 15:29:54 +0100 Subject: [PATCH 13/38] _get_dora_results() ajout prefetch pour labels de financement --- back/dora/services/search.py | 1 + 1 file changed, 1 insertion(+) diff --git a/back/dora/services/search.py b/back/dora/services/search.py index 294a08a11..23f14790c 100644 --- a/back/dora/services/search.py +++ b/back/dora/services/search.py @@ -253,6 +253,7 @@ def _get_dora_results( "location_kinds", "categories", "subcategories", + "funding_labels", "coach_orientation_modes", "beneficiaries_access_modes", ) From 88dd4b11bc812decac18b18bb0b14c1555d27bd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Gounot?= Date: Mon, 4 Nov 2024 15:30:12 +0100 Subject: [PATCH 14/38] SearchResultsSerializer: ajout labels de financement --- back/dora/services/serializers.py | 1 + 1 file changed, 1 insertion(+) diff --git a/back/dora/services/serializers.py b/back/dora/services/serializers.py index 62d890e93..40ec679a6 100644 --- a/back/dora/services/serializers.py +++ b/back/dora/services/serializers.py @@ -800,6 +800,7 @@ class Meta: "diffusion_zone_type", "distance", "fee_condition", + "funding_labels", "is_orientable", "kinds", "location_kinds", From 4de548ec5a4a14c46d3ba9c9b866090d94529c6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Gounot?= Date: Mon, 4 Nov 2024 17:23:46 +0100 Subject: [PATCH 15/38] =?UTF-8?q?Page=20de=20r=C3=A9sultats=20de=20recherc?= =?UTF-8?q?he=20:=20utilise=20les=20labels=20de=20financement=20fournis=20?= =?UTF-8?q?par=20le=20backend?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- front/src/lib/types.ts | 6 +++ front/src/routes/recherche/+page.svelte | 54 ++++++++----------- front/src/routes/recherche/+page.ts | 4 +- .../routes/recherche/map-view-button.svelte | 8 +-- .../routes/recherche/result-filters.svelte | 25 +++------ 5 files changed, 44 insertions(+), 53 deletions(-) diff --git a/front/src/lib/types.ts b/front/src/lib/types.ts index 6ef96f9c2..7336c9fb6 100644 --- a/front/src/lib/types.ts +++ b/front/src/lib/types.ts @@ -306,6 +306,11 @@ export type BeneficiaryAccessModes = | "telephoner" | "autre"; +export interface FundingLabel { + value: string; + label: string; +} + export interface SearchQuery { categoryIds: string[]; subCategoryIds: string[]; @@ -335,6 +340,7 @@ export interface ServiceSearchResult { isOrientable?: boolean; coachOrientationModes?: string[]; beneficiariesAccessModes?: string[]; + fundingLabels: Array; modificationDate: string; name: string; shortDesc: string; diff --git a/front/src/routes/recherche/+page.svelte b/front/src/routes/recherche/+page.svelte index 76707f7d0..ca50b9879 100644 --- a/front/src/routes/recherche/+page.svelte +++ b/front/src/routes/recherche/+page.svelte @@ -6,7 +6,6 @@ import Breadcrumb from "$lib/components/display/breadcrumb.svelte"; import CenteredGrid from "$lib/components/display/centered-grid.svelte"; import SearchForm from "$lib/components/specialized/service-search.svelte"; - import { FUNDED_SERVICES } from "$lib/consts"; import type { ServiceSearchResult } from "$lib/types"; import { userInfo } from "$lib/utils/auth"; import { isInDeploymentDepartments } from "$lib/utils/misc"; @@ -15,10 +14,7 @@ import DoraDeploymentNotice from "./dora-deployment-notice.svelte"; import OnlyNationalResultsNotice from "./only-national-results-notice.svelte"; import ServiceSuggestionNotice from "./service-suggestion-notice.svelte"; - import ResultFilters, { - type Filters, - type FundedByDepartment, - } from "./result-filters.svelte"; + import ResultFilters, { type Filters } from "./result-filters.svelte"; import MapViewButton from "./map-view-button.svelte"; import ResultCount from "./result-count.svelte"; import SearchResults from "./search-results.svelte"; @@ -28,7 +24,7 @@ const FILTER_KEY_TO_QUERY_PARAM = { kinds: "kinds", - fundedBy: "fundedBy", + fundingLabels: "fundingLabels", feeConditions: "fees", locationKinds: "locs", }; @@ -55,7 +51,12 @@ }); function resetFilters() { - filters = { kinds: [], fundedBy: [], feeConditions: [], locationKinds: [] }; + filters = { + kinds: [], + fundingLabels: [], + feeConditions: [], + locationKinds: [], + }; } // Réinitialise les filtres quand la recherche est actualisée. @@ -76,10 +77,10 @@ filters.kinds.length === 0 || (service.kinds && filters.kinds.some((value) => service.kinds!.includes(value))); - const fundedByMatch = - filters.fundedBy.length === 0 || - filters.fundedBy.some((department) => - FUNDED_SERVICES[department].slugs.includes(service.slug) + const fundingLabelsMatch = + filters.fundingLabels.length === 0 || + filters.fundingLabels.some((value) => + service.fundingLabels.includes(value) ); const feeConditionMatch = filters.feeConditions.length === 0 || @@ -98,7 +99,7 @@ ); return ( kindsMatch && - fundedByMatch && + fundingLabelsMatch && feeConditionMatch && locationKindsMatch && onSiteAndNearby @@ -134,20 +135,6 @@ data.cityCode && !isInDeploymentDepartments(data.cityCode, data.servicesOptions); - $: fundedByDepartment = - data.cityCode && - (Object.keys(FUNDED_SERVICES).find((department) => - data.cityCode?.startsWith(department) - ) as FundedByDepartment | undefined); - $: fundedByOptions = fundedByDepartment - ? [ - { - value: fundedByDepartment, - label: FUNDED_SERVICES[fundedByDepartment].organism, - }, - ] - : []; - $: showMesAidesDialog = !$userInfo && data.categoryIds.includes("mobilite"); @@ -180,14 +167,19 @@ -
+
@@ -217,7 +209,7 @@
{/if} -
+
diff --git a/front/src/routes/recherche/+page.ts b/front/src/routes/recherche/+page.ts index 50a3a96eb..2a435b97a 100644 --- a/front/src/routes/recherche/+page.ts +++ b/front/src/routes/recherche/+page.ts @@ -22,6 +22,7 @@ async function getResults({ lon, }: SearchQuery): Promise<{ cityBounds: [number, number, number, number]; + fundingLabels: Array<{value: string, label: string}>; services: ServiceSearchResult[]; }> { const querystring = getQueryString({ @@ -71,7 +72,7 @@ export const load: PageLoad = async ({ url, parent }) => { const lon = query.get("lon"); const lat = query.get("lat"); - const { cityBounds, services } = await getResults({ + const { cityBounds, fundingLabels, services } = await getResults({ // La priorité est donnée aux sous-catégories categoryIds: subCategoryIds.length ? [] : categoryIds, subCategoryIds, @@ -116,6 +117,7 @@ export const load: PageLoad = async ({ url, parent }) => { cityBounds, cityCode, cityLabel, + fundingLabels, label, lat, lon, diff --git a/front/src/routes/recherche/map-view-button.svelte b/front/src/routes/recherche/map-view-button.svelte index a25e74431..c45ffe9a3 100644 --- a/front/src/routes/recherche/map-view-button.svelte +++ b/front/src/routes/recherche/map-view-button.svelte @@ -1,19 +1,19 @@ @@ -36,13 +27,13 @@ choices={servicesOptions.kinds} bind:group={filters.kinds} /> - {#key fundedByOptions} - {#if fundedByOptions.length > 0} + {#key fundingLabels} + {#if fundingLabels.length > 0} {/if} {/key} From 44579550af9df2e3c0a9a8c3bf2305c9ab9804a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Gounot?= Date: Mon, 4 Nov 2024 17:33:07 +0100 Subject: [PATCH 16/38] Fiche service : utilise les labels de financement fournis par le backend --- .../services/display/service-key-informations.svelte | 10 ++-------- front/src/lib/types.ts | 4 ++++ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/front/src/lib/components/specialized/services/display/service-key-informations.svelte b/front/src/lib/components/specialized/services/display/service-key-informations.svelte index 23ee9ce6d..0f10c0885 100644 --- a/front/src/lib/components/specialized/services/display/service-key-informations.svelte +++ b/front/src/lib/components/specialized/services/display/service-key-informations.svelte @@ -1,5 +1,4 @@ -
+

Résultats de votre recherche

-
+
{#each filteredServices as service, index} {#if noPagination || index < currentPageLength} Date: Tue, 12 Nov 2024 18:09:01 +0100 Subject: [PATCH 25/38] =?UTF-8?q?search=5Fservices()=20et=20=5Fget=5Fdora?= =?UTF-8?q?=5Fresults():=20ajout=20du=20param=C3=A8tre=20funding=5Flabels?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Utilisé par les recherches sauvegardées --- back/dora/services/search.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/back/dora/services/search.py b/back/dora/services/search.py index 23f14790c..642289011 100644 --- a/back/dora/services/search.py +++ b/back/dora/services/search.py @@ -239,6 +239,7 @@ def _get_dora_results( kinds: Optional[list[str]] = None, fees: Optional[list[str]] = None, location_kinds: Optional[list[str]] = None, + funding_labels: Optional[list[str]] = None, lat: Optional[float] = None, lon: Optional[float] = None, ): @@ -276,6 +277,9 @@ def _get_dora_results( if location_kinds: services = services.filter(location_kinds__value__in=location_kinds) + if funding_labels: + services = services.filter(funding_labels__value__in=funding_labels) + with_remote = not location_kinds or "a-distance" in location_kinds with_onsite = not location_kinds or "en-presentiel" in location_kinds @@ -315,11 +319,13 @@ def _get_dora_results( with_onsite, ) - funding_labels = FundingLabel.objects.filter(service__in=results).distinct() + funding_labels_found = FundingLabel.objects.filter(service__in=results).distinct() return SearchResultSerializer( results, many=True, context={"request": request} - ).data, {"funding_labels": FundingLabelSerializer(funding_labels, many=True).data} + ).data, { + "funding_labels": FundingLabelSerializer(funding_labels_found, many=True).data + } def search_services( @@ -331,6 +337,7 @@ def search_services( kinds: Optional[list[str]] = None, fees: Optional[list[str]] = None, location_kinds: Optional[list[str]] = None, + funding_labels: Optional[list[str]] = None, di_client: Optional[data_inclusion.DataInclusionClient] = None, lat: Optional[float] = None, lon: Optional[float] = None, @@ -373,6 +380,7 @@ def search_services( kinds=kinds, fees=fees, location_kinds=location_kinds, + funding_labels=funding_labels, lat=lat, lon=lon, ) From c2273417dafbf08162a52946d758eadecc83ffe2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Gounot?= Date: Tue, 12 Nov 2024 18:09:43 +0100 Subject: [PATCH 26/38] SavedSearch: utilisation de funding_labels pour la recherche --- back/dora/services/models.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/back/dora/services/models.py b/back/dora/services/models.py index 8eb39719b..af75df8c8 100644 --- a/back/dora/services/models.py +++ b/back/dora/services/models.py @@ -723,13 +723,17 @@ def get_recent_services(self, cutoff_date): if self.location_kinds.exists(): location_kinds = self.location_kinds.values_list("value", flat=True) + funding_labels = None + if self.funding_labels.exists(): + funding_labels = self.funding_labels.values_list("value", flat=True) + # Récupération des résultats de la recherche from .search import search_services city_code = arrdt_to_main_insee_code(self.city_code) city = get_object_or_404(City, pk=city_code) - results = search_services( + results, metadata = search_services( None, self.city_code, city, @@ -738,6 +742,7 @@ def get_recent_services(self, cutoff_date): kinds, fees, location_kinds, + funding_labels, di_client, ) From e54738d338880ffe2d0ce6fae1d2161e515280f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Gounot?= Date: Tue, 12 Nov 2024 18:10:10 +0100 Subject: [PATCH 27/38] SavedSearchSerializer: ajout des champs funding_labels et funding_labels_display --- back/dora/services/serializers.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/back/dora/services/serializers.py b/back/dora/services/serializers.py index b565c8908..3382340f4 100644 --- a/back/dora/services/serializers.py +++ b/back/dora/services/serializers.py @@ -710,6 +710,8 @@ class Meta: "kinds_display", "location_kinds", "location_kinds_display", + "funding_labels", + "funding_labels_display", "new_services_count", ] From 4908b4af48ea9cdc59feea47e5cdc57920c449ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Gounot?= Date: Tue, 12 Nov 2024 18:11:53 +0100 Subject: [PATCH 28/38] =?UTF-8?q?test=5Fservices.py:=20prise=20en=20compte?= =?UTF-8?q?=20du=20nouveau=20champ=20funding=5Flabels=20retourn=C3=A9=20pa?= =?UTF-8?q?r=20la=20recherche?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- back/dora/services/tests/test_services.py | 102 +++++++++++----------- 1 file changed, 53 insertions(+), 49 deletions(-) diff --git a/back/dora/services/tests/test_services.py b/back/dora/services/tests/test_services.py index 0e685e1a9..6bb7bab32 100644 --- a/back/dora/services/tests/test_services.py +++ b/back/dora/services/tests/test_services.py @@ -1067,7 +1067,7 @@ def test_find_services_in_city(self): request = self.factory.get("/search/", {"city": self.city1.code}) response = self.search(request) assert response.status_code == 200 - assert len(response.data) == 2 + assert len(response.data) == 3 assert len(response.data["services"]) == 1 assert response.data["services"][0]["id"] == service_data["id"] @@ -1080,9 +1080,11 @@ def test_dont_find_services_in_other_city(self): response = self.search(request) assert response.status_code == 200 - assert len(response.data) == 2 + assert len(response.data) == 3 assert len(response.data["services"]) == 0 - assert ["city_bounds", "services"] == list(response.data.keys()) + assert ["city_bounds", "funding_labels", "services"] == list( + response.data.keys() + ) def test_filter_by_fee(self): service_data = self.make_di_service( @@ -1099,7 +1101,7 @@ def test_filter_by_fee(self): ) response = self.search(request) assert response.status_code == 200 - assert len(response.data) == 2 + assert len(response.data) == 3 assert response.data["services"][0]["id"] == service_data["id"] def test_filter_by_kind(self): @@ -1119,7 +1121,7 @@ def test_filter_by_kind(self): ) response = self.search(request) assert response.status_code == 200 - assert len(response.data) == 2 + assert len(response.data) == 3 assert len(response.data["services"]) == 1 assert response.data["services"][0]["id"] == service_data["id"] @@ -1143,7 +1145,7 @@ def test_filter_by_cat(self): ) response = self.search(request) assert response.status_code == 200 - assert len(response.data) == 2 + assert len(response.data) == 3 assert len(response.data["services"]) == 2 assert response.data["services"][0]["id"] in [ service_data_1["id"], @@ -1161,7 +1163,7 @@ def test_simple_search_with_data_inclusion(self): response = self.search(request) assert response.status_code == 200 - assert len(response.data) == 2 + assert len(response.data) == 3 assert len(response.data["services"]) == 1 assert response.data["services"][0]["distance"] == 0 assert response.data["services"][0]["id"] == service_data["id"] @@ -1178,7 +1180,7 @@ def test_simple_search_with_data_inclusion_and_dora(self): d = response.data assert response.status_code == 200 - assert len(d) == 2 + assert len(d) == 3 assert len(d["services"]) == 2 assert service_dora.slug in [d["services"][0]["slug"], d["services"][1]["slug"]] assert service_data["id"] in [ @@ -1202,8 +1204,10 @@ def search_services(self, **kwargs): response = self.search(request, di_client) assert response.status_code == 200 # ajout des "city bounds" pour la carte - assert len(response.data) == 2 - assert ["city_bounds", "services"] == list(response.data.keys()) + assert len(response.data) == 3 + assert ["city_bounds", "funding_labels", "services"] == list( + response.data.keys() + ) service, *_ = response.data["services"] assert service["slug"] == service_dora.slug @@ -1232,7 +1236,7 @@ def test_search_target_sources(self): response = self.search(request) assert response.status_code == 200 - assert len(response.data) == 2 + assert len(response.data) == 3 assert len(response.data["services"]) == 1 assert response.data["services"][0]["id"] == service_data["id"] @@ -1760,7 +1764,7 @@ def test_can_see_published_services(self): ) response = self.client.get(f"/search/?city={self.city1.code}") assert response.status_code == 200 - assert len(response.data) == 2 + assert len(response.data) == 3 assert len(response.data["services"]) == 1 assert response.data["services"][0]["slug"] == service.slug @@ -1770,7 +1774,7 @@ def test_cant_see_draft_services(self): ) response = self.client.get(f"/search/?city={self.city1.code}") assert response.status_code == 200 - assert len(response.data) == 2 + assert len(response.data) == 3 assert len(response.data["services"]) == 0 def test_cant_see_suggested_services(self): @@ -1780,7 +1784,7 @@ def test_cant_see_suggested_services(self): ) response = self.client.get(f"/search/?city={self.city1.code}") assert response.status_code == 200 - assert len(response.data) == 2 + assert len(response.data) == 3 assert len(response.data["services"]) == 0 def test_can_see_service_with_future_suspension_date(self): @@ -1791,7 +1795,7 @@ def test_can_see_service_with_future_suspension_date(self): ) response = self.client.get(f"/search/?city={self.city1.code}") assert response.status_code == 200 - assert len(response.data) == 2 + assert len(response.data) == 3 assert len(response.data["services"]) == 1 assert response.data["services"][0]["slug"] == service.slug @@ -1803,7 +1807,7 @@ def test_cannot_see_service_with_past_suspension_date(self): ) response = self.client.get(f"/search/?city={self.city1.code}") assert response.status_code == 200 - assert len(response.data) == 2 + assert len(response.data) == 3 assert len(response.data["services"]) == 0 def test_find_services_in_city(self): @@ -1814,7 +1818,7 @@ def test_find_services_in_city(self): ) response = self.client.get(f"/search/?city={self.city1.code}") assert response.status_code == 200 - assert len(response.data) == 2 + assert len(response.data) == 3 assert len(response.data["services"]) == 1 assert response.data["services"][0]["slug"] == service.slug @@ -1826,7 +1830,7 @@ def test_find_services_in_epci(self): ) response = self.client.get(f"/search/?city={self.city1.code}") assert response.status_code == 200 - assert len(response.data) == 2 + assert len(response.data) == 3 assert len(response.data["services"]) == 1 assert response.data["services"][0]["slug"] == service.slug @@ -1838,7 +1842,7 @@ def test_find_services_in_dept(self): ) response = self.client.get(f"/search/?city={self.city1.code}") assert response.status_code == 200 - assert len(response.data) == 2 + assert len(response.data) == 3 assert len(response.data["services"]) == 1 assert response.data["services"][0]["slug"] == service.slug @@ -1850,7 +1854,7 @@ def test_find_services_in_region(self): ) response = self.client.get(f"/search/?city={self.city1.code}") assert response.status_code == 200 - assert len(response.data) == 2 + assert len(response.data) == 3 assert len(response.data["services"]) == 1 assert response.data["services"][0]["slug"] == service.slug @@ -1862,7 +1866,7 @@ def test_dont_find_services_in_other_city(self): ) response = self.client.get(f"/search/?city={self.city2.code}") assert response.status_code == 200 - assert len(response.data) == 2 + assert len(response.data) == 3 assert len(response.data["services"]) == 0 def test_dont_find_services_in_other_epci(self): @@ -1873,7 +1877,7 @@ def test_dont_find_services_in_other_epci(self): ) response = self.client.get(f"/search/?city={self.city2.code}") assert response.status_code == 200 - assert len(response.data) == 2 + assert len(response.data) == 3 assert len(response.data["services"]) == 0 def test_dont_find_services_in_other_department(self): @@ -1884,7 +1888,7 @@ def test_dont_find_services_in_other_department(self): ) response = self.client.get(f"/search/?city={self.city2.code}") assert response.status_code == 200 - assert len(response.data) == 2 + assert len(response.data) == 3 assert len(response.data["services"]) == 0 def test_dont_find_services_in_other_region(self): @@ -1895,7 +1899,7 @@ def test_dont_find_services_in_other_region(self): ) response = self.client.get(f"/search/?city={self.city2.code}") assert response.status_code == 200 - assert len(response.data) == 2 + assert len(response.data) == 3 assert len(response.data["services"]) == 0 def test_filter_by_fee_free(self): @@ -1918,7 +1922,7 @@ def test_filter_by_fee_free(self): ) response = self.client.get(f"/search/?city={self.city1.code}&fees=gratuit") assert response.status_code == 200 - assert len(response.data) == 2 + assert len(response.data) == 3 assert len(response.data["services"]) == 1 assert response.data["services"][0]["slug"] == service1.slug @@ -1942,7 +1946,7 @@ def test_filter_by_fee_payant(self): ) response = self.client.get(f"/search/?city={self.city1.code}&fees=payant") assert response.status_code == 200 - assert len(response.data) == 2 + assert len(response.data) == 3 assert len(response.data["services"]) == 1 assert response.data["services"][0]["slug"] == service2.slug @@ -1968,7 +1972,7 @@ def test_filter_by_fee_gratuit_sous_condition(self): f"/search/?city={self.city1.code}&fees=gratuit-sous-conditions" ) assert response.status_code == 200 - assert len(response.data) == 2 + assert len(response.data) == 3 assert len(response.data["services"]) == 1 assert response.data["services"][0]["slug"] == service3.slug @@ -1992,7 +1996,7 @@ def test_filter_without_fee(self): ) response = self.client.get(f"/search/?city={self.city1.code}") assert response.status_code == 200 - assert len(response.data) == 2 + assert len(response.data) == 3 assert len(response.data["services"]) == 3 def test_filter_kinds_one(self): @@ -2011,7 +2015,7 @@ def test_filter_kinds_one(self): f"/search/?city={self.city1.code}&kinds={allowed_kinds[0].value}" ) assert response.status_code == 200 - assert len(response.data) == 2 + assert len(response.data) == 3 assert len(response.data["services"]) == 1 assert response.data["services"][0]["slug"] == service1.slug @@ -2036,7 +2040,7 @@ def test_filter_kinds_several(self): f"/search/?city={self.city1.code}&kinds={allowed_kinds[1].value},{allowed_kinds[2].value}" ) assert response.status_code == 200 - assert len(response.data) == 2 + assert len(response.data) == 3 assert len(response.data["services"]) == 2 response_slugs = [r["slug"] for r in response.data["services"]] @@ -2059,7 +2063,7 @@ def test_filter_kinds_nomatch(self): f"/search/?city={self.city1.code}&kinds={allowed_kinds[3].value}" ) assert response.status_code == 200 - assert len(response.data) == 2 + assert len(response.data) == 3 assert len(response.data["services"]) == 0 def test_find_service_with_requested_cat(self): @@ -2070,7 +2074,7 @@ def test_find_service_with_requested_cat(self): ) response = self.client.get(f"/search/?city={self.city1.code}&cats=cat1") assert response.status_code == 200 - assert len(response.data) == 2 + assert len(response.data) == 3 assert len(response.data["services"]) == 1 assert response.data["services"][0]["slug"] == service.slug @@ -2087,7 +2091,7 @@ def test_find_service_with_requested_cats(self): ) response = self.client.get(f"/search/?city={self.city1.code}&cats=cat1,cat2") assert response.status_code == 200 - assert len(response.data) == 2 + assert len(response.data) == 3 assert len(response.data["services"]) == 2 response_slugs = sorted([s["slug"] for s in response.data["services"]]) @@ -2111,7 +2115,7 @@ def test_find_service_with_requested_cats_exclude_one(self): ) response = self.client.get(f"/search/?city={self.city1.code}&cats=cat1,cat2") assert response.status_code == 200 - assert len(response.data) == 2 + assert len(response.data) == 3 assert len(response.data["services"]) == 2 response_slugs = sorted([s["slug"] for s in response.data["services"]]) @@ -2126,7 +2130,7 @@ def test_dont_find_service_without_requested_cat(self): response = self.client.get(f"/search/?city={self.city1.code}&cats=cat2") assert response.status_code == 200 - assert len(response.data) == 2 + assert len(response.data) == 3 assert len(response.data["services"]) == 0 def test_find_service_with_requested_subcat(self): @@ -2137,7 +2141,7 @@ def test_find_service_with_requested_subcat(self): ) response = self.client.get(f"/search/?city={self.city1.code}&subs=cat1--sub1") assert response.status_code == 200 - assert len(response.data) == 2 + assert len(response.data) == 3 assert len(response.data["services"]) == 1 assert response.data["services"][0]["slug"] == service.slug @@ -2156,7 +2160,7 @@ def test_find_service_with_requested_subcats(self): f"/search/?city={self.city1.code}&subs=cat1--sub1,cat1--sub2" ) assert response.status_code == 200 - assert len(response.data) == 2 + assert len(response.data) == 3 assert len(response.data["services"]) == 2 response_slugs = sorted([s["slug"] for s in response.data["services"]]) @@ -2188,7 +2192,7 @@ def test_find_service_with_requested_subcats_different_cats(self): ) assert response.status_code == 200 - assert len(response.data) == 2 + assert len(response.data) == 3 assert len(response.data["services"]) == 3 response_slugs = sorted([s["slug"] for s in response.data["services"]]) @@ -2216,7 +2220,7 @@ def test_find_service_with_requested_subcats_exclude_one(self): f"/search/?city={self.city1.code}&subs=cat1--sub1,cat1--sub2" ) assert response.status_code == 200 - assert len(response.data) == 2 + assert len(response.data) == 3 assert len(response.data["services"]) == 2 response_slugs = sorted([s["slug"] for s in response.data["services"]]) @@ -2231,7 +2235,7 @@ def test_dont_find_service_without_requested_subcat(self): response = self.client.get(f"/search/?city={self.city1.code}&subs=cat1--sub2") assert response.status_code == 200 - assert len(response.data) == 2 + assert len(response.data) == 3 assert len(response.data["services"]) == 0 def test_find_service_with_no_subcat_when_looking_for_the__other__subcat(self): @@ -2244,7 +2248,7 @@ def test_find_service_with_no_subcat_when_looking_for_the__other__subcat(self): ) response = self.client.get(f"/search/?city={self.city1.code}&subs=cat1--autre") assert response.status_code == 200 - assert len(response.data) == 2 + assert len(response.data) == 3 assert len(response.data["services"]) == 1 assert response.data["services"][0]["slug"] == service.slug @@ -2261,7 +2265,7 @@ def test_find_service_with_no_subcat_when_looking_for_the__other__subcat_2( ) response = self.client.get(f"/search/?city={self.city1.code}&subs=cat1--autre") assert response.status_code == 200 - assert len(response.data) == 2 + assert len(response.data) == 3 assert len(response.data["services"]) == 1 assert response.data["services"][0]["slug"] == service.slug @@ -2273,7 +2277,7 @@ def test_dont_find_service_with_no_subcat_when_looking_for_any_subcat(self): ) response = self.client.get(f"/search/?city={self.city1.code}&subs=cat1--sub1") assert response.status_code == 200 - assert len(response.data) == 2 + assert len(response.data) == 3 assert len(response.data["services"]) == 0 def test_find_cats_and_subcats_are_independant(self): @@ -2293,7 +2297,7 @@ def test_find_cats_and_subcats_are_independant(self): f"/search/?city={self.city1.code}&cats=cat1&subs=cat2--sub1" ) assert response.status_code == 200 - assert len(response.data) == 2 + assert len(response.data) == 3 assert len(response.data["services"]) == 2 @@ -2438,7 +2442,7 @@ def test_distance_no_more_than_100km(self): service2.location_kinds.set([LocationKind.objects.get(value="en-presentiel")]) response = self.client.get("/search/?city=31555") - assert len(response.data) == 2 + assert len(response.data) == 3 assert len(response.data["services"]) == 1 def test_displayed_if_remote_and_onsite_more_than_100km(self): @@ -2457,7 +2461,7 @@ def test_displayed_if_remote_and_onsite_more_than_100km(self): ) response = self.client.get("/search/?city=31555") - assert len(response.data) == 2 + assert len(response.data) == 3 assert len(response.data["services"]) == 1 def test_displayed_only_once_if_remote_and_onsite_less_than_100km(self): @@ -2476,7 +2480,7 @@ def test_displayed_only_once_if_remote_and_onsite_less_than_100km(self): ) response = self.client.get("/search/?city=31555") - assert len(response.data) == 2 + assert len(response.data) == 3 assert len(response.data["services"]) == 1 def test_intercalate_remote(self): @@ -2779,7 +2783,7 @@ def test_archives_dont_appear_in_search_results_anon(self): response = self.client.get(f"/search/?city={city.code}") assert response.status_code == 200 - assert len(response.data) == 2 + assert len(response.data) == 3 assert len(response.data["services"]) == 0 def test_archives_dont_appear_in_search_results_auth(self): @@ -2792,7 +2796,7 @@ def test_archives_dont_appear_in_search_results_auth(self): response = self.client.get(f"/search/?city={city.code}") assert response.status_code == 200 - assert len(response.data) == 2 + assert len(response.data) == 3 assert len(response.data["services"]) == 0 From ce381b9e4476d67b0fcc1c68a9911d05f71c87f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Gounot?= Date: Tue, 12 Nov 2024 18:14:25 +0100 Subject: [PATCH 29/38] search-results.svelte: correction du formattage --- front/src/routes/recherche/search-results.svelte | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/front/src/routes/recherche/search-results.svelte b/front/src/routes/recherche/search-results.svelte index 85ca40a32..84c996d7f 100644 --- a/front/src/routes/recherche/search-results.svelte +++ b/front/src/routes/recherche/search-results.svelte @@ -88,9 +88,9 @@ } -
+

Résultats de votre recherche

-
+
{#each filteredServices as service, index} {#if noPagination || index < currentPageLength} Date: Tue, 12 Nov 2024 18:55:28 +0100 Subject: [PATCH 30/38] Page recherche: renommage de l'argument de filtre pour les labels de financement en funding --- front/src/routes/recherche/+page.svelte | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/front/src/routes/recherche/+page.svelte b/front/src/routes/recherche/+page.svelte index c590df721..08698ce27 100644 --- a/front/src/routes/recherche/+page.svelte +++ b/front/src/routes/recherche/+page.svelte @@ -24,7 +24,7 @@ const FILTER_KEY_TO_QUERY_PARAM = { kinds: "kinds", - fundingLabels: "fundingLabels", + fundingLabels: "funding", feeConditions: "fees", locationKinds: "locs", }; @@ -167,9 +167,9 @@ -
+