From 19f6298a7d25c425f0b07e6da47a0e1192c67230 Mon Sep 17 00:00:00 2001 From: Nicolas Oudard Date: Thu, 5 Dec 2024 17:05:30 +0100 Subject: [PATCH 1/2] Ajout de DRF --- .secrets.baseline | 4 ++-- core/settings.py | 1 + qfdmo/serializers/acteur.py | 9 +++++++++ requirements.in | 1 + requirements.txt | 5 +++++ 5 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 qfdmo/serializers/acteur.py diff --git a/.secrets.baseline b/.secrets.baseline index 5a53f6b0d..622d49497 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -151,7 +151,7 @@ "filename": "core/settings.py", "hashed_secret": "1ee34e26aeaf89c64ecc2c85efe6a961b75a50e9", "is_verified": false, - "line_number": 211 + "line_number": 212 } ], "docker-compose.yml": [ @@ -207,5 +207,5 @@ } ] }, - "generated_at": "2024-12-04T10:44:03Z" + "generated_at": "2024-12-05T16:05:16Z" } diff --git a/core/settings.py b/core/settings.py index 52480fce0..9caf480b6 100644 --- a/core/settings.py +++ b/core/settings.py @@ -57,6 +57,7 @@ "qfdmd", "qfdmo", "corsheaders", + "djangorestframework", ] # FIXME : check if we can manage django forms templating with jinja2 diff --git a/qfdmo/serializers/acteur.py b/qfdmo/serializers/acteur.py new file mode 100644 index 000000000..f99898b25 --- /dev/null +++ b/qfdmo/serializers/acteur.py @@ -0,0 +1,9 @@ +from rest_framework import serializers + +from qfdmo.models.acteur import Acteur + + +class ActeurSerializer(serializers.ModelSerializer): + class Meta: + model = Acteur + fields = "__all__" diff --git a/requirements.in b/requirements.in index cee5c7ae6..bcfc31c9f 100644 --- a/requirements.in +++ b/requirements.in @@ -8,6 +8,7 @@ django-import-export[xls, xlsx] django-ninja django-sql-explorer django-storages +djangorestframework gunicorn Jinja2 opening-hours-py diff --git a/requirements.txt b/requirements.txt index bfb3cc579..c6131ee3e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -154,6 +154,7 @@ django==5.1.1 \ # django-ninja # django-sql-explorer # django-storages + # djangorestframework django-colorfield==0.11.0 \ --hash=sha256:05c38c8eb2a94938b810a19b2011846391a4ce71d1c92e88a35974fbcc8fc62e \ --hash=sha256:460f40e6123b6ae0fb51a4eb86fc258fcdc0ea28f75102b685e8209b1eae9ec3 @@ -196,6 +197,10 @@ django-widget-tweaks==1.5.0 \ # via # -r requirements.in # django-dsfr +djangorestframework==3.15.2 \ + --hash=sha256:2b8871b062ba1aefc2de01f773875441a961fefbf79f5eed1e32b2f096944b20 \ + --hash=sha256:36fe88cd2d6c6bec23dca9804bab2ba5517a8bb9d8f47ebc68981b56840107ad + # via -r requirements.in et-xmlfile==1.1.0 \ --hash=sha256:8eb9e2bc2f8c97e37a2dc85a09ecdcdec9d8a396530a6d5a33b30b9a92da0c5c \ --hash=sha256:a2ba85d1d6a74ef63837eed693bcb89c3f752169b0e3e7ae5b16ca5e1b3deada From 345c77179182babbc6b68f38b305759953bf575e Mon Sep 17 00:00:00 2001 From: Nicolas Oudard Date: Mon, 9 Dec 2024 08:06:47 +0100 Subject: [PATCH 2/2] Changer les FormView pour des ListView dans la vue addresse --- core/settings.py | 2 +- core/utils.py | 2 +- integration_tests/qfdmo/test_adresses_view.py | 17 +- .../qfdmo/test_reemploi_solution.py | 10 +- qfdmo/models/acteur.py | 1 - qfdmo/views/adresses.py | 365 +++++++++--------- unit_tests/qfdmo/test_addresses_view.py | 5 +- 7 files changed, 200 insertions(+), 202 deletions(-) diff --git a/core/settings.py b/core/settings.py index 9caf480b6..f71bdcd56 100644 --- a/core/settings.py +++ b/core/settings.py @@ -57,7 +57,7 @@ "qfdmd", "qfdmo", "corsheaders", - "djangorestframework", + "rest_framework", ] # FIXME : check if we can manage django forms templating with jinja2 diff --git a/core/utils.py b/core/utils.py index dbe3eb4db..f2ffa1eb5 100644 --- a/core/utils.py +++ b/core/utils.py @@ -3,7 +3,7 @@ from qfdmo.models.action import get_directions -def get_direction(request, is_carte=False): +def get_direction(request, is_carte: bool = False) -> str | None: default_direction = None if is_carte else settings.DEFAULT_ACTION_DIRECTION direction = request.GET.get("direction", default_direction) if direction not in [d["code"] for d in get_directions()]: diff --git a/integration_tests/qfdmo/test_adresses_view.py b/integration_tests/qfdmo/test_adresses_view.py index 1a30c1617..f84384845 100644 --- a/integration_tests/qfdmo/test_adresses_view.py +++ b/integration_tests/qfdmo/test_adresses_view.py @@ -76,7 +76,7 @@ def proposition_service_donner(action_donner, sous_categorie): @pytest.fixture def displayed_acteur_reparer(proposition_service_reparer): displayed_acteur = DisplayedActeurFactory( - exclusivite_de_reprisereparation=True, + exclusivite_de_reprisereparation=False, location=Point(1, 1), statut=ActeurStatus.ACTIF, ) @@ -86,7 +86,6 @@ def displayed_acteur_reparer(proposition_service_reparer): @pytest.fixture def displayed_acteur_reparacteur(displayed_acteur_reparer): - reparacteur = LabelQualiteFactory(code="reparacteur") displayed_acteur_reparer.labels.add(reparacteur) return displayed_acteur_reparer @@ -171,12 +170,11 @@ def test_filtre_reparacteur_return_0_acteur( "action_list": [action_reparer.code], "latitude": [1], "longitude": [1], - "label_reparacteur": ["true"], + "label_reparacteur": ["on"], } ) adresses_view.setup(request) context = adresses_view.get_context_data() - assert context["acteurs"].count() == 0 def test_filtre_reparacteur_return_1_reparacteur( @@ -188,12 +186,11 @@ def test_filtre_reparacteur_return_1_reparacteur( "action_list": [action_reparer.code], "latitude": [1], "longitude": [1], - "label_reparacteur": ["true"], + "label_reparacteur": ["on"], } ) adresses_view.setup(request) context = adresses_view.get_context_data() - assert context["acteurs"].count() == 1 def test_filtre_reparacteur_return_1_noreparacteur( @@ -209,7 +206,7 @@ def test_filtre_reparacteur_return_1_noreparacteur( "action_list": [f"{action_reparer.code}|{action_donner.code}"], "latitude": [1], "longitude": [1], - "label_reparacteur": ["true"], + "label_reparacteur": ["on"], } ) adresses_view.setup(request) @@ -279,13 +276,15 @@ def test_no_action_reparer_excludes_acteurs_avec_exclusivite( def test_action_reparer_excludes_acteurs_avec_exclusivite_by_default( self, adresses_view, displayed_acteur_reparer, action_reparer, action_preter ): + displayed_acteur_reparer.exclusivite_de_reprisereparation = True + displayed_acteur_reparer.save() request = HttpRequest() request.GET = query_dict_from( { "action_list": [f"{action_reparer.code}|{action_preter.code}"], "latitude": [1], "longitude": [1], - "pas_exclusivite_reparation": ["true"], + "pas_exclusivite_reparation": ["on"], } ) adresses_view.setup(request) @@ -355,7 +354,7 @@ def test_action_filters( "action_list": [f"{action_reparer.code}|{action_preter.code}"], "latitude": [1], "longitude": [1], - "sc_id": [sous_categorie.id], + "sc_id": [str(sous_categorie.id)], "carte": carte, } ) diff --git a/integration_tests/qfdmo/test_reemploi_solution.py b/integration_tests/qfdmo/test_reemploi_solution.py index b22c065af..b48bc61e5 100644 --- a/integration_tests/qfdmo/test_reemploi_solution.py +++ b/integration_tests/qfdmo/test_reemploi_solution.py @@ -26,10 +26,10 @@ class TestInitialValue: "digital": "0", "latitude": None, "longitude": None, - "label_reparacteur": None, - "ess": None, - "bonus": None, - "bounding_box": None, + "label_reparacteur": False, + "ess": False, + "bonus": False, + "bounding_box": "", "pas_exclusivite_reparation": True, "action_displayed": ( "preter|emprunter|louer|mettreenlocation|reparer|donner|echanger" @@ -39,7 +39,6 @@ class TestInitialValue: "preter|emprunter|louer|mettreenlocation|reparer|donner|echanger" "|acheter|revendre" ), - "epci_codes": [], } def test_formulaire_without_parameters(self, client): @@ -85,4 +84,5 @@ def test_carte_without_parameters(self, client): "acheter|revendre", "trier", ], + "epci_codes": [], } diff --git a/qfdmo/models/acteur.py b/qfdmo/models/acteur.py index 8b1027960..469c0cb01 100644 --- a/qfdmo/models/acteur.py +++ b/qfdmo/models/acteur.py @@ -229,7 +229,6 @@ def in_bbox(self, bbox): def from_center(self, longitude, latitude): reference_point = Point(float(longitude), float(latitude), srid=4326) distance_in_degrees = settings.DISTANCE_MAX / 111320 - return ( self.physical() .annotate(distance=Distance("location", reference_point)) diff --git a/qfdmo/views/adresses.py b/qfdmo/views/adresses.py index 48647eb9c..e4a7bcac9 100644 --- a/qfdmo/views/adresses.py +++ b/qfdmo/views/adresses.py @@ -12,12 +12,12 @@ from django.db.models.functions import Length, Lower from django.db.models.query import QuerySet from django.forms import model_to_dict -from django.http import JsonResponse +from django.http import HttpRequest, JsonResponse from django.shortcuts import redirect, render from django.urls.base import reverse from django.utils.safestring import mark_safe from django.views.decorators.http import require_GET -from django.views.generic.edit import FormView +from django.views.generic.list import ListView from core.jinja2_handler import distance_to_acteur from core.utils import get_direction @@ -96,136 +96,74 @@ def get_context_data(self, **kwargs): return context -class SearchActeursView( +class ActeursListView( DigitalMixin, TurboFormMixin, - FormView, + ListView, ): # TODO : supprimer is_iframe = False is_carte = False is_embedded = True - def get_initial(self): - initial = super().get_initial() - # TODO: refacto forms : delete this line - initial["sous_categorie_objet"] = self.request.GET.get("sous_categorie_objet") - # TODO: refacto forms : delete this line - initial["adresse"] = self.request.GET.get("adresse") - initial["digital"] = self.request.GET.get("digital", "0") - initial["direction"] = get_direction(self.request, self.is_carte) - # TODO: refacto forms : delete this line - initial["latitude"] = self.request.GET.get("latitude") - # TODO: refacto forms : delete this line - initial["longitude"] = self.request.GET.get("longitude") - # TODO: refacto forms : delete this line - initial["label_reparacteur"] = self.request.GET.get("label_reparacteur") - initial["epci_codes"] = self.request.GET.getlist("epci_codes") - initial["pas_exclusivite_reparation"] = self.request.GET.get( - "pas_exclusivite_reparation", True - ) - # TODO: refacto forms : delete this line - initial["bonus"] = self.request.GET.get("bonus") - # TODO: refacto forms : delete this line - initial["ess"] = self.request.GET.get("ess") - # TODO: refacto forms : delete this line - initial["bounding_box"] = self.request.GET.get("bounding_box") - initial["sc_id"] = ( - self.request.GET.get("sc_id") if initial["sous_categorie_objet"] else None - ) - - # Action to display and check - action_displayed = self._set_action_displayed() - initial["action_displayed"] = "|".join([a.code for a in action_displayed]) - - action_list = self._set_action_list(action_displayed) - initial["action_list"] = "|".join([a.code for a in action_list]) + action_displayed: str = "" + action_list: str = "" + adresse: str | None = "" + bonus: bool = False + bounding_box: str = "" + digital: str = "0" + direction: str | None = None + epci_codes: str = "" + ess: bool = False + grouped_action: list[str] = [] # list ? + label_reparacteur: bool = False + latitude: float | None = 0.0 + legend_grouped_action: list[str] = [] # list ? + longitude: float | None = 0.0 + pas_exclusivite_reparation: bool = False + sc_id: int | None = None + sous_categorie_objet: str = "" + request: HttpRequest - if self.is_carte: - grouped_action_choices = self._get_grouped_action_choices(action_displayed) - actions_to_select = self._get_selected_action() - initial["grouped_action"] = self._grouped_action_from( - grouped_action_choices, actions_to_select - ) - # TODO : refacto forms, merge with grouped_action field - initial["legend_grouped_action"] = initial["grouped_action"] + def setup(self, request, *args, **kwargs): - initial["action_list"] = "|".join( - [a for ga in initial["grouped_action"] for a in ga.split("|")] - ) + # FIXME : on peut certainement faire mieux + self.request = request - return initial - - def get_form(self, form_class=None): - if self.request.GET & self.get_form_class().base_fields.keys(): - # TODO: refacto forms we should use a bounded form in this case - # Here we check that the request shares some parameters - # with the fields in the form. If this happens, this might - # means that we are badly using request instead of a bounded - # form and that we need to access a validated form. - # - # This case happens when the form is loaded inside a turbo-frame. - # form = self.get_form_class()(self.request.GET) - form = super().get_form(form_class) - else: - form = super().get_form(form_class) + self.sous_categorie_objet = request.GET.get("sous_categorie_objet") + self.adresse = request.GET.get("adresse") + self.digital = request.GET.get("digital", "0") + self.direction = get_direction(request, self.is_carte) + self.latitude = request.GET.get("latitude") + self.longitude = request.GET.get("longitude") + self.label_reparacteur = bool(request.GET.get("label_reparacteur")) + self.epci_codes = request.GET.getlist("epci_codes") - action_displayed = self._set_action_displayed() if self.is_carte else None - grouped_action_choices = ( - self._get_grouped_action_choices(action_displayed) - if action_displayed - else None + self.pas_exclusivite_reparation = bool( + request.GET.get("pas_exclusivite_reparation", True) ) - - form.load_choices( - self.request, - grouped_action_choices=grouped_action_choices, - disable_reparer_option=( - "reparer" not in form.initial.get("grouped_action", []) - ), + self.bonus = bool(request.GET.get("bonus")) + self.ess = bool(request.GET.get("ess")) + self.bounding_box = request.GET.get("bounding_box", "") + self.sc_id = ( + int(request.GET.get("sc_id", "")) + if request.GET.get("sc_id", "").isnumeric() + else None ) - return form - - def get_data_from_request_or_bounded_form(self, key: str, default=None): - """Temporary dummy method - - There is a flaw in the way the form is instantiated, because the - form is never bounded to its data. - The request is directly used to perform various tasks, like - populating some multiple choice field choices, hence missing all - the validation provided by django forms. + return super().setup(request, *args, **kwargs) - To prepare a future refactor of this form, the method here calls - the cleaned_data when the form is bounded and the request.GET - QueryDict when it is not bounded. - Note : we call getlist and not get because in some cases, the request - parameters needs to be treated as a list. - - The form is currently used for various use cases: - - The map form - - The "iframe form" form (for https://epargnonsnosressources.gouv.fr) - - The turbo-frames - The form should be bounded at least when used in turbo-frames. - - The name is explicitely very verbose because it is not meant to stay - a long time as is. - - TODO: refacto forms : get rid of this method and use cleaned_data when - form is valid and request.GET for non-field request parameters""" - try: - return self.cleaned_data.get(key, default) - except AttributeError: - pass - - try: - return self.request.GET.get(key, default) - except AttributeError: - return self.request.GET.getlist(key, default) + def get_queryset(self): + # Manage the selection of sous_categorie_objet and actions + acteurs = self._acteurs_from_sous_categorie_objet_and_actions() + if self.digital == "1": + acteurs = acteurs.digital()[:100] + else: + bbox, acteurs = self._bbox_and_acteurs_from_location_or_epci(acteurs) + acteurs = acteurs[: self._get_max_displayed_acteurs()] + return acteurs def get_context_data(self, **kwargs): - form = self.get_form_class()(self.request.GET) - kwargs.update( # TODO: refacto forms : define a BooleanField carte on CarteAddressesForm carte=self.is_carte, @@ -234,16 +172,10 @@ def get_context_data(self, **kwargs): location="{}", ) - if form.is_valid(): - self.cleaned_data = form.cleaned_data - else: - # TODO : refacto forms : handle this case properly - self.cleaned_data = form.cleaned_data - # Manage the selection of sous_categorie_objet and actions acteurs = self._acteurs_from_sous_categorie_objet_and_actions() - if self.get_data_from_request_or_bounded_form("digital") == "1": + if self.digital == "1": acteurs = acteurs.digital()[:100] else: bbox, acteurs = self._bbox_and_acteurs_from_location_or_epci(acteurs) @@ -251,35 +183,27 @@ def get_context_data(self, **kwargs): # Set Home location (address set as input) # FIXME : can be manage in template using the form value ? - if ( - latitude := self.get_data_from_request_or_bounded_form("latitude") - ) and ( - longitude := self.get_data_from_request_or_bounded_form("longitude") - ): + if (latitude := self.latitude) and (longitude := self.longitude): kwargs.update( location=json.dumps({"latitude": latitude, "longitude": longitude}) ) - - kwargs.update(acteurs=acteurs) - context = super().get_context_data(**kwargs) - # TODO : refacto forms, gérer ça autrement try: if bbox is None: - context["form"].initial["bounding_box"] = None + kwargs["form"].initial["bounding_box"] = None except NameError: pass - return context + kwargs.update(acteurs=acteurs) + # FIXME : on peut certainement faire mieux + kwargs.update(object_list=acteurs) + return super().get_context_data(**kwargs) def _bbox_and_acteurs_from_location_or_epci(self, acteurs): - custom_bbox = cast( - str, self.get_data_from_request_or_bounded_form("bounding_box") - ) + custom_bbox = cast(str, self.bounding_box) center = center_from_leaflet_bbox(custom_bbox) - latitude = center[1] or self.get_data_from_request_or_bounded_form("latitude") - longitude = center[0] or self.get_data_from_request_or_bounded_form("longitude") - + latitude = center[1] or self.latitude + longitude = center[0] or self.longitude if custom_bbox: bbox = sanitize_leaflet_bbox(custom_bbox) acteurs_in_bbox = acteurs.in_bbox(bbox) @@ -292,13 +216,12 @@ def _bbox_and_acteurs_from_location_or_epci(self, acteurs): # - Tester cas avec center retourné par leaflet if latitude and longitude: acteurs_from_center = acteurs.from_center(longitude, latitude) - if acteurs_from_center.count(): custom_bbox = None return custom_bbox, acteurs_from_center - if epci_codes := self.get_data_from_request_or_bounded_form("epci_codes"): + if epci_codes := self.epci_codes: geojson_list = [retrieve_epci_geojson(code) for code in epci_codes] bbox = bbox_from_list_of_geojson(geojson_list, buffer=0) if geojson_list: @@ -310,8 +233,9 @@ def _bbox_and_acteurs_from_location_or_epci(self, acteurs): return custom_bbox, acteurs.none() def _get_max_displayed_acteurs(self): - if self.request.GET.get("limit", "").isnumeric(): - return int(self.request.GET.get("limit")) + limit = self.request.GET.get("limit", "") + if limit.isnumeric(): + return int(limit) if self.is_carte: return settings.CARTE_MAX_SOLUTION_DISPLAYED return settings.DEFAULT_MAX_SOLUTION_DISPLAYED @@ -330,13 +254,11 @@ def _set_action_displayed(self) -> List[Action]: for action in cached_action_instances if direction in [d.code for d in action.directions.all()] ] - if action_displayed := self.get_data_from_request_or_bounded_form( - "action_displayed", "" - ): + if self.action_displayed: cached_action_instances = [ action for action in cached_action_instances - if action.code in action_displayed.split("|") + if action.code in self.action_displayed.split("|") ] # In form mode, only display actions with afficher=True # TODO : discuss with epargnonsnosressources if we can remove this condition @@ -348,7 +270,7 @@ def _set_action_displayed(self) -> List[Action]: return cached_action_instances def _set_action_list(self, action_displayed: List[Action]) -> List[Action]: - if action_list := self.get_data_from_request_or_bounded_form("action_list", ""): + if action_list := self.request.GET.get("action_list", ""): return [ action for action in action_displayed @@ -363,26 +285,20 @@ def _get_selected_action_code(self): # FIXME : est-ce possible d'optimiser en accédant au valeur initial du form ? # selection from interface - if self.get_data_from_request_or_bounded_form("grouped_action"): + if self.grouped_action: return [ code - for new_groupe_action in self.get_data_from_request_or_bounded_form( - "grouped_action" - ) + for new_groupe_action in self.grouped_action for code in new_groupe_action.split("|") ] # Selection is not set in interface, get all available from # (checked_)action_list - if self.get_data_from_request_or_bounded_form("action_list"): - return self.get_data_from_request_or_bounded_form("action_list", "").split( - "|" - ) + if self.action_list: + return self.action_list.split("|") # Selection is not set in interface, defeult checked action list is not set # get all available from action_displayed - if self.get_data_from_request_or_bounded_form("action_displayed"): - return self.get_data_from_request_or_bounded_form( - "action_displayed", "" - ).split("|") + if self.action_displayed: + return self.action_displayed.split("|") # return empty array, will search in all actions return [] @@ -399,19 +315,19 @@ def _get_selected_action(self) -> List[Action]: for new_groupe_action in self.request.GET.getlist("grouped_action") for code in new_groupe_action.split("|") ] + # Selection is not set in interface, get all available from # (checked_)action_list - elif action_list := self.cleaned_data.get("action_list"): + elif action_list := self.action_list: # TODO : effet de bord si la list des action n'est pas cohérente avec # les actions affichées # il faut collecté les actions coché selon les groupes d'action codes = action_list.split("|") # Selection is not set in interface, defeult checked action list is not set # get all available from action_displayed - elif action_displayed := self.cleaned_data.get("action_displayed"): + elif action_displayed := self.action_displayed: codes = action_displayed.split("|") # return empty array, will search in all actions - # Cast needed because of the cache cached_action_instances = cast( List[Action], cache.get_or_set("action_instances", get_action_instances) @@ -437,9 +353,7 @@ def _acteurs_from_sous_categorie_objet_and_actions( filters, excludes = self._compile_acteurs_queryset( reparer_is_checked, selected_actions_ids, reparer_action_id ) - - acteurs = DisplayedActeur.objects.filter(filters).exclude(excludes) - + acteurs = DisplayedActeur.objects.exclude(excludes).filter(filters) if reparer_is_checked: acteurs = acteurs.with_reparer().with_bonus() @@ -458,30 +372,23 @@ def _compile_acteurs_queryset( filters = Q(statut=ActeurStatus.ACTIF) excludes = Q() - if ( - self.get_data_from_request_or_bounded_form("pas_exclusivite_reparation") - is not False - or not reparer_is_checked - ): + if self.pas_exclusivite_reparation and reparer_is_checked: excludes |= Q(exclusivite_de_reprisereparation=True) - if self.get_data_from_request_or_bounded_form("ess"): + if self.ess: filters &= Q(labels__code="ess") - if self.get_data_from_request_or_bounded_form("bonus"): + if self.bonus: filters &= Q(labels__bonus=True) - if sous_categorie_id := self.get_data_from_request_or_bounded_form("sc_id", 0): + if sous_categorie_id := self.sc_id: filters &= Q( proposition_services__sous_categories__id=sous_categorie_id, ) actions_filters = Q() - if ( - self.get_data_from_request_or_bounded_form("label_reparacteur") - and reparer_is_checked - ): + if self.label_reparacteur and reparer_is_checked: selected_actions_ids = [ a for a in selected_actions_ids if a != reparer_action_id ] @@ -549,21 +456,78 @@ def _grouped_action_from( ] -class CarteSearchActeursView(SearchActeursView): +class CarteSearchActeursView(ActeursListView): is_carte = True template_name = "qfdmo/carte.html" - form_class = CarteForm + + def setup(self, request, *args, **kwargs): + parent_setup = super().setup(request, *args, **kwargs) + + # Action to display and check + action_displayed = self._set_action_displayed() + self.action_displayed = "|".join([a.code for a in action_displayed]) + + action_list = self._set_action_list(action_displayed) + self.action_list = "|".join([a.code for a in action_list]) + + if self.is_carte: + grouped_action_choices = self._get_grouped_action_choices(action_displayed) + actions_to_select = self._get_selected_action() + self.grouped_action = self._grouped_action_from( + grouped_action_choices, actions_to_select + ) + # TODO : refacto forms, merge with grouped_action field + self.legend_grouped_action = self.grouped_action + + self.action_list = "|".join( + [a for ga in self.grouped_action for a in ga.split("|")] + ) + + # TODO : simplifiable avec le setup + grouped_action_choices = self._get_grouped_action_choices( + self._set_action_displayed() + ) + + self.form = CarteForm( + initial={ + "direction": self.direction, + "adresse": self.adresse, + "digital": self.digital, + "latitude": self.latitude, + "longitude": self.longitude, + "action_displayed": self.action_displayed, + "action_list": self.action_list, + "label_reparacteur": self.label_reparacteur, + "epci_codes": self.epci_codes, + "pas_exclusivite_reparation": self.pas_exclusivite_reparation, + "bonus": self.bonus, + "ess": self.ess, + "bounding_box": self.bounding_box, + "sc_id": self.sc_id, + "grouped_action": self.grouped_action, + "legend_grouped_action": self.legend_grouped_action, + "sous_categorie_objet": self.sous_categorie_objet, + } + ) + + self.form.load_choices( + self.request, + grouped_action_choices=grouped_action_choices, + disable_reparer_option=( + "reparer" not in self.form.initial.get("grouped_action", []) + ), + ) + + return parent_setup def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - context.update(is_carte=True) - return context + return super().get_context_data(**kwargs, form=self.form, is_carte=True) def _get_selected_action_ids(self): return [a.id for a in self._get_selected_action()] -class FormulaireSearchActeursView(SearchActeursView): +class FormulaireSearchActeursView(ActeursListView): """Affiche le formulaire utilisé sur epargnonsnosressources.gouv.fr Cette vue est à considérer en mode maintenance uniquement et ne doit pas être modifiée.""" @@ -572,6 +536,41 @@ class FormulaireSearchActeursView(SearchActeursView): template_name = "qfdmo/formulaire.html" form_class = FormulaireForm + def setup(self, request, *args, **kwargs): + parent_setup = super().setup(request, *args, **kwargs) + + # Action to display and check + action_displayed = self._set_action_displayed() + self.action_displayed = "|".join([a.code for a in action_displayed]) + + action_list = self._set_action_list(action_displayed) + self.action_list = "|".join([a.code for a in action_list]) + + self.form = FormulaireForm( + initial={ + "direction": self.direction, + "adresse": self.adresse, + "digital": self.digital, + "latitude": self.latitude, + "longitude": self.longitude, + "action_displayed": self.action_displayed, + "action_list": self.action_list, + "label_reparacteur": self.label_reparacteur, + "pas_exclusivite_reparation": self.pas_exclusivite_reparation, + "bonus": self.bonus, + "ess": self.ess, + "bounding_box": self.bounding_box, + "sc_id": self.sc_id, + "sous_categorie_objet": self.sous_categorie_objet, + } + ) + self.form.load_choices(self.request) + + return parent_setup + + def get_context_data(self, **kwargs): + return super().get_context_data(**kwargs, form=self.form, is_carte=False) + def _get_selected_action_ids(self): # TODO: merge this method with the one from CarteSearchActeursView # and do not return a list of dict but a queryset instead diff --git a/unit_tests/qfdmo/test_addresses_view.py b/unit_tests/qfdmo/test_addresses_view.py index c667abfe3..cf7bc66b9 100644 --- a/unit_tests/qfdmo/test_addresses_view.py +++ b/unit_tests/qfdmo/test_addresses_view.py @@ -1,5 +1,5 @@ import pytest -from django.http import HttpRequest +from django.http import HttpRequest, QueryDict from qfdmo.views.adresses import CarteSearchActeursView, FormulaireSearchActeursView from unit_tests.core.test_utils import query_dict_from @@ -68,7 +68,8 @@ class TestFormulaireViewGetActionList: @pytest.mark.django_db def test_get_action_list(self, params, action_list): request = HttpRequest() - request.GET = params + query_string = "&".join([f"{key}={value}" for key, value in params.items()]) + request.GET = QueryDict(query_string) adresses_view = FormulaireSearchActeursView() adresses_view.setup(request)