diff --git a/config/tests/test_urls.py b/config/tests/test_urls.py index 19439fcdd..74e16e842 100644 --- a/config/tests/test_urls.py +++ b/config/tests/test_urls.py @@ -30,7 +30,6 @@ def site() -> Site: HAIE_URLS = [ "triage", - "triage_result", ] AMENAGEMENT_URLS = [ diff --git a/config/urls_haie.py b/config/urls_haie.py index 18480b12d..ef5f62a91 100644 --- a/config/urls_haie.py +++ b/config/urls_haie.py @@ -1,8 +1,9 @@ from django.urls import include, path +from django.utils.translation import gettext_lazy as _ from .urls import urlpatterns as common_urlpatterns urlpatterns = [ path("", include("envergo.pages.urls_haie")), - path("indre/", include("envergo.moulinette.urls_haie")), + path(_("moulinette/"), include("envergo.moulinette.urls_haie")), ] + common_urlpatterns diff --git a/envergo/evaluations/forms.py b/envergo/evaluations/forms.py index 3b3c4d18b..ac156be02 100644 --- a/envergo/evaluations/forms.py +++ b/envergo/evaluations/forms.py @@ -8,7 +8,7 @@ from phonenumber_field.formfields import PhoneNumberField from envergo.evaluations.models import USER_TYPES, Request -from envergo.evaluations.utils import extract_department +from envergo.evaluations.utils import extract_department_from_address from envergo.evaluations.validators import application_number_validator from envergo.geodata.models import Department from envergo.utils.fields import NoIdnEmailField @@ -119,7 +119,7 @@ def clean(self): department_input = data.get("department", None) if not department_input: # extract department from address - department_input = extract_department(address) + department_input = extract_department_from_address(address) if department_input and department_input not in address: # when a town is selected on its own, without a complete address, there is no zip code. @@ -128,10 +128,10 @@ def clean(self): department = ( Department.objects.filter(department=department_input) - .select_related("moulinette_config") + .select_related("configamenagement") .first() ) - if department and not department.is_activated(): + if department and not department.is_amenagement_activated(): self.add_error( "department", ValidationError( diff --git a/envergo/evaluations/models.py b/envergo/evaluations/models.py index 3e82d63cb..61f92eaa3 100644 --- a/envergo/evaluations/models.py +++ b/envergo/evaluations/models.py @@ -262,7 +262,7 @@ def get_moulinette_config(self): lng, lat = params["lng"], params["lat"] coords = Point(float(lng), float(lat), srid=EPSG_WGS84) department = Department.objects.filter(geometry__contains=coords).first() - return department.moulinette_config if department else None + return department.configamenagement if department else None def get_moulinette(self): """Return the moulinette instance for this evaluation.""" diff --git a/envergo/evaluations/tests/test_admin.py b/envergo/evaluations/tests/test_admin.py index 28d142eb1..47df0c2b9 100644 --- a/envergo/evaluations/tests/test_admin.py +++ b/envergo/evaluations/tests/test_admin.py @@ -7,7 +7,10 @@ generate_reference, ) from envergo.evaluations.tests.factories import EvaluationFactory, RequestFactory -from envergo.moulinette.tests.factories import CriterionFactory, MoulinetteConfigFactory +from envergo.moulinette.tests.factories import ( + ConfigAmenagementFactory, + CriterionFactory, +) pytestmark = pytest.mark.django_db @@ -92,7 +95,7 @@ def test_create_eval_fails_when_it_already_exists(client, admin_user, eval_reque def test_evaluation_email_sending(admin_client, evaluation, mailoutbox): # Make sure the "loi sur l'eau" result will be set CriterionFactory() - MoulinetteConfigFactory() + ConfigAmenagementFactory() url = reverse("admin:evaluations_evaluation_email_avis", args=[evaluation.pk]) res = admin_client.get(url) @@ -126,7 +129,7 @@ def test_evaluation_email_sending(admin_client, evaluation, mailoutbox): def test_evaluation_email_throttling(admin_client, evaluation, mailoutbox): # Make sure the "loi sur l'eau" result will be set CriterionFactory() - MoulinetteConfigFactory() + ConfigAmenagementFactory() url = reverse("admin:evaluations_evaluation_email_avis", args=[evaluation.pk]) res = admin_client.get(url) @@ -156,7 +159,7 @@ def test_evaluation_email_throttling(admin_client, evaluation, mailoutbox): def test_evaluation_email_recipient_overriding(admin_client, evaluation, mailoutbox): # Make sure the "loi sur l'eau" result will be set CriterionFactory() - MoulinetteConfigFactory() + ConfigAmenagementFactory() url = reverse("admin:evaluations_evaluation_email_avis", args=[evaluation.pk]) res = admin_client.get(url) @@ -180,7 +183,7 @@ def test_evaluation_email_recipient_overriding(admin_client, evaluation, mailout def test_evaluation_email_with_empty_recipients(admin_client, evaluation, mailoutbox): # Make sure the "loi sur l'eau" result will be set CriterionFactory() - MoulinetteConfigFactory() + ConfigAmenagementFactory() url = reverse("admin:evaluations_evaluation_email_avis", args=[evaluation.pk]) res = admin_client.get(url) diff --git a/envergo/evaluations/tests/test_eval_emails.py b/envergo/evaluations/tests/test_eval_emails.py index a1beea03d..7a5330eba 100644 --- a/envergo/evaluations/tests/test_eval_emails.py +++ b/envergo/evaluations/tests/test_eval_emails.py @@ -9,8 +9,8 @@ from envergo.geodata.conftest import bizous_town_center, france_map, france_zh # noqa from envergo.moulinette.regulations import RequiredAction, Stake from envergo.moulinette.tests.factories import ( + ConfigAmenagementFactory, CriterionFactory, - MoulinetteConfigFactory, PerimeterFactory, RegulationFactory, ) @@ -25,7 +25,7 @@ def override_settings(settings): @pytest.fixture(autouse=True) def moulinette_config(france_map, france_zh, loire_atlantique_department): # noqa - MoulinetteConfigFactory( + ConfigAmenagementFactory( department=loire_atlantique_department, is_activated=True, ddtm_water_police_email="ddtm_email_test@example.org", diff --git a/envergo/evaluations/tests/test_forms.py b/envergo/evaluations/tests/test_forms.py index 5b4af7541..f2fb2179c 100644 --- a/envergo/evaluations/tests/test_forms.py +++ b/envergo/evaluations/tests/test_forms.py @@ -2,7 +2,7 @@ from envergo.evaluations.forms import WizardAddressForm from envergo.geodata.conftest import loire_atlantique_department # noqa -from envergo.moulinette.tests.factories import MoulinetteConfigFactory +from envergo.moulinette.tests.factories import ConfigAmenagementFactory pytestmark = pytest.mark.django_db @@ -17,7 +17,7 @@ def form_data(): @pytest.fixture(autouse=True) def moulinette_config(loire_atlantique_department): # noqa - MoulinetteConfigFactory( + ConfigAmenagementFactory( department=loire_atlantique_department, is_activated=True, ddtm_water_police_email="ddtm_email_test@example.org", diff --git a/envergo/evaluations/tests/test_models.py b/envergo/evaluations/tests/test_models.py index 43234cd58..aab24a686 100644 --- a/envergo/evaluations/tests/test_models.py +++ b/envergo/evaluations/tests/test_models.py @@ -8,8 +8,8 @@ from envergo.geodata.conftest import loire_atlantique_department # noqa from envergo.geodata.conftest import bizous_town_center, france_map # noqa from envergo.moulinette.tests.factories import ( + ConfigAmenagementFactory, CriterionFactory, - MoulinetteConfigFactory, PerimeterFactory, RegulationFactory, ) @@ -19,7 +19,7 @@ @pytest.fixture(autouse=True) def moulinette_config(france_map, loire_atlantique_department): # noqa - MoulinetteConfigFactory( + ConfigAmenagementFactory( department=loire_atlantique_department, is_activated=True, ddtm_water_police_email="ddtm_email_test@example.org", diff --git a/envergo/evaluations/tests/test_views.py b/envergo/evaluations/tests/test_views.py index 0a4aa2ac0..b36123fa9 100644 --- a/envergo/evaluations/tests/test_views.py +++ b/envergo/evaluations/tests/test_views.py @@ -14,7 +14,7 @@ VersionFactory, ) from envergo.geodata.conftest import loire_atlantique_department # noqa -from envergo.moulinette.tests.factories import MoulinetteConfigFactory +from envergo.moulinette.tests.factories import ConfigAmenagementFactory pytestmark = pytest.mark.django_db @@ -26,7 +26,7 @@ def autouse_site(site): @pytest.fixture() def moulinette_config(loire_atlantique_department): # noqa - MoulinetteConfigFactory( + ConfigAmenagementFactory( department=loire_atlantique_department, is_activated=True, ddtm_water_police_email="ddtm_email_test@example.org", @@ -37,7 +37,7 @@ def moulinette_config(loire_atlantique_department): # noqa @pytest.fixture() def unactivated_moulinette_config(loire_atlantique_department): # noqa - MoulinetteConfigFactory( + ConfigAmenagementFactory( department=loire_atlantique_department, is_activated=False, ddtm_water_police_email="ddtm_email_test@example.org", diff --git a/envergo/evaluations/utils.py b/envergo/evaluations/utils.py index 6a7ec5f87..e83160554 100644 --- a/envergo/evaluations/utils.py +++ b/envergo/evaluations/utils.py @@ -14,11 +14,15 @@ def extract_postal_code(address): return None -def extract_department(address): +def extract_department_from_address(address): """Extract the department as two (or three) digits from a stringified address. return None if no department is found. """ postal_code = extract_postal_code(address) + return extract_department_from_postal_code(postal_code) + + +def extract_department_from_postal_code(postal_code): department = None if postal_code: department = postal_code[:2] @@ -33,5 +37,4 @@ def extract_department(address): department = "2A" # Corse-du-Sud elif 20200 <= code_number <= 20620: department = "2B" # Haute-Corse - return department diff --git a/envergo/geodata/models.py b/envergo/geodata/models.py index 88a849032..441a1f782 100644 --- a/envergo/geodata/models.py +++ b/envergo/geodata/models.py @@ -160,8 +160,8 @@ class Meta: def __str__(self): return self.get_department_display() - def is_activated(self): - config = getattr(self, "moulinette_config", None) + def is_amenagement_activated(self): + config = getattr(self, "configamenagement", None) return config and config.is_activated diff --git a/envergo/moulinette/admin.py b/envergo/moulinette/admin.py index 9d2e0c665..00a616a41 100644 --- a/envergo/moulinette/admin.py +++ b/envergo/moulinette/admin.py @@ -8,8 +8,9 @@ from envergo.geodata.admin import DepartmentsListFilter from envergo.moulinette.models import ( REGULATIONS, + ConfigAmenagement, + ConfigHaie, Criterion, - MoulinetteConfig, MoulinetteTemplate, Perimeter, Regulation, @@ -250,7 +251,7 @@ def departments(self, obj): return obj.activation_map.departments -class MoulinetteConfigForm(forms.ModelForm): +class ConfigAmenagementForm(forms.ModelForm): regulations_available = forms.MultipleChoiceField( label=_("Regulations available"), required=False, choices=REGULATIONS ) @@ -276,7 +277,7 @@ def clean_criteria_values(self): class MoulinetteConfigTemplateForm(forms.ModelForm): - """Form to edit a MoulinetteTemplate in a MoulinetteConfig. + """Form to edit a MoulinetteTemplate in a ConfigAmenagement. We remove every key that is not a real template (autorisation_urba_*, etc.) """ @@ -295,10 +296,10 @@ class MoulinetteConfigTemplateInline(MoulinetteTemplateInline): form = MoulinetteConfigTemplateForm -@admin.register(MoulinetteConfig) -class MoulinetteConfigAdmin(admin.ModelAdmin): +@admin.register(ConfigAmenagement) +class ConfigAmenagementAdmin(admin.ModelAdmin): list_display = ["department", "is_activated", "zh_doubt"] - form = MoulinetteConfigForm + form = ConfigAmenagementForm inlines = [MoulinetteConfigTemplateInline] list_filter = ["is_activated", "zh_doubt"] @@ -315,3 +316,17 @@ def get_queryset(self, request): class MoulinetteTemplateAdmin(admin.ModelAdmin): list_display = ["config", "key"] search_fields = ["content"] + + +@admin.register(ConfigHaie) +class ConfigHaieAdmin(admin.ModelAdmin): + list_display = ["department", "is_activated", "department_guichet_unique_url"] + list_filter = ["is_activated"] + + def get_queryset(self, request): + qs = super().get_queryset(request) + return ( + qs.select_related("department") + .order_by("department__department") + .defer("department__geometry") + ) diff --git a/envergo/moulinette/forms/__init__.py b/envergo/moulinette/forms/__init__.py index d0bcf0cde..67d86d8c1 100644 --- a/envergo/moulinette/forms/__init__.py +++ b/envergo/moulinette/forms/__init__.py @@ -229,7 +229,6 @@ class TriageFormHaie(forms.Form): department = DisplayCharField( label="Département", required=True, - initial="36", get_display_value=lambda x: dict(DEPARTMENT_CHOICES).get(x, "Inconnu"), ) element = DisplayChoiceField( diff --git a/envergo/moulinette/migrations/0059_alter_moulinettetemplate_key_haiedepartmentconfig.py b/envergo/moulinette/migrations/0059_alter_moulinettetemplate_key_haiedepartmentconfig.py new file mode 100644 index 000000000..d94f02de2 --- /dev/null +++ b/envergo/moulinette/migrations/0059_alter_moulinettetemplate_key_haiedepartmentconfig.py @@ -0,0 +1,584 @@ +# Generated by Django 4.2.13 on 2024-09-30 13:55 + +from django.db import migrations, models +import django.db.models.deletion + +class Migration(migrations.Migration): + + dependencies = [ + ("geodata", "0016_alter_department_geometry"), + ("moulinette", "0058_alter_moulinetteconfig_regulations_available_and_more"), + ] + + operations = [ + migrations.AlterField( + model_name="moulinettetemplate", + name="key", + field=models.CharField( + choices=[ + ("autorisation_urba_pa", "autorisation_urba_pa"), + ( + "autorisation_urba_pa_lotissement", + "autorisation_urba_pa_lotissement", + ), + ("autorisation_urba_pc", "autorisation_urba_pc"), + ( + "autorisation_urba_amenagement_dp", + "autorisation_urba_amenagement_dp", + ), + ( + "autorisation_urba_construction_dp", + "autorisation_urba_construction_dp", + ), + ("autorisation_urba_none", "autorisation_urba_none"), + ("autorisation_urba_other", "autorisation_urba_other"), + ( + "conditionnalite_pac/bcae8_interdit_amenagement.html", + "conditionnalite_pac/bcae8_interdit_amenagement.html", + ), + ( + "conditionnalite_pac/bcae8_interdit_autre.html", + "conditionnalite_pac/bcae8_interdit_autre.html", + ), + ( + "conditionnalite_pac/bcae8_interdit_chemin_acces.html", + "conditionnalite_pac/bcae8_interdit_chemin_acces.html", + ), + ( + "conditionnalite_pac/bcae8_interdit_meilleur_emplacement.html", + "conditionnalite_pac/bcae8_interdit_meilleur_emplacement.html", + ), + ( + "conditionnalite_pac/bcae8_interdit_transfert_parcelles.html", + "conditionnalite_pac/bcae8_interdit_transfert_parcelles.html", + ), + ( + "conditionnalite_pac/bcae8_non_soumis.html", + "conditionnalite_pac/bcae8_non_soumis.html", + ), + ( + "conditionnalite_pac/bcae8_non_soumis_petit.html", + "conditionnalite_pac/bcae8_non_soumis_petit.html", + ), + ( + "conditionnalite_pac/bcae8_soumis_amenagement.html", + "conditionnalite_pac/bcae8_soumis_amenagement.html", + ), + ( + "conditionnalite_pac/bcae8_soumis_autre.html", + "conditionnalite_pac/bcae8_soumis_autre.html", + ), + ( + "conditionnalite_pac/bcae8_soumis_chemin_acces.html", + "conditionnalite_pac/bcae8_soumis_chemin_acces.html", + ), + ( + "conditionnalite_pac/bcae8_soumis_meilleur_emplacement.html", + "conditionnalite_pac/bcae8_soumis_meilleur_emplacement.html", + ), + ( + "conditionnalite_pac/bcae8_soumis_remplacement.html", + "conditionnalite_pac/bcae8_soumis_remplacement.html", + ), + ( + "conditionnalite_pac/bcae8_soumis_transfert_parcelles.html", + "conditionnalite_pac/bcae8_soumis_transfert_parcelles.html", + ), + ("dep/dep_soumis.html", "dep/dep_soumis.html"), + ( + "eval_env/aire_de_stationnement_cas_par_cas.html", + "eval_env/aire_de_stationnement_cas_par_cas.html", + ), + ( + "eval_env/aire_de_stationnement_non_soumis.html", + "eval_env/aire_de_stationnement_non_soumis.html", + ), + ( + "eval_env/autres_rubriques_non_disponible.html", + "eval_env/autres_rubriques_non_disponible.html", + ), + ( + "eval_env/camping_cas_par_cas.html", + "eval_env/camping_cas_par_cas.html", + ), + ( + "eval_env/camping_non_soumis.html", + "eval_env/camping_non_soumis.html", + ), + ( + "eval_env/camping_systematique.html", + "eval_env/camping_systematique.html", + ), + ( + "eval_env/clause_filet_clause_filet.html", + "eval_env/clause_filet_clause_filet.html", + ), + ( + "eval_env/defrichement_deboisement_cas_par_cas.html", + "eval_env/defrichement_deboisement_cas_par_cas.html", + ), + ( + "eval_env/defrichement_deboisement_non_soumis.html", + "eval_env/defrichement_deboisement_non_soumis.html", + ), + ( + "eval_env/emprise_cas_par_cas.html", + "eval_env/emprise_cas_par_cas.html", + ), + ( + "eval_env/emprise_non_soumis.html", + "eval_env/emprise_non_soumis.html", + ), + ( + "eval_env/emprise_systematique.html", + "eval_env/emprise_systematique.html", + ), + ( + "eval_env/photovoltaique_cas_par_cas_sol.html", + "eval_env/photovoltaique_cas_par_cas_sol.html", + ), + ( + "eval_env/photovoltaique_cas_par_cas_toiture.html", + "eval_env/photovoltaique_cas_par_cas_toiture.html", + ), + ( + "eval_env/photovoltaique_non_soumis.html", + "eval_env/photovoltaique_non_soumis.html", + ), + ( + "eval_env/photovoltaique_non_soumis_ombriere.html", + "eval_env/photovoltaique_non_soumis_ombriere.html", + ), + ( + "eval_env/photovoltaique_non_soumis_toiture.html", + "eval_env/photovoltaique_non_soumis_toiture.html", + ), + ( + "eval_env/photovoltaique_systematique_sol.html", + "eval_env/photovoltaique_systematique_sol.html", + ), + ( + "eval_env/photovoltaique_systematique_toiture.html", + "eval_env/photovoltaique_systematique_toiture.html", + ), + ( + "eval_env/piste_cyclable_cas_par_cas.html", + "eval_env/piste_cyclable_cas_par_cas.html", + ), + ( + "eval_env/piste_cyclable_non_soumis.html", + "eval_env/piste_cyclable_non_soumis.html", + ), + ( + "eval_env/premier_boisement_cas_par_cas.html", + "eval_env/premier_boisement_cas_par_cas.html", + ), + ( + "eval_env/premier_boisement_non_soumis.html", + "eval_env/premier_boisement_non_soumis.html", + ), + ( + "eval_env/result_cas_par_cas.html", + "eval_env/result_cas_par_cas.html", + ), + ( + "eval_env/result_non_active.html", + "eval_env/result_non_active.html", + ), + ( + "eval_env/result_non_disponible.html", + "eval_env/result_non_disponible.html", + ), + ( + "eval_env/result_non_soumis.html", + "eval_env/result_non_soumis.html", + ), + ( + "eval_env/result_systematique.html", + "eval_env/result_systematique.html", + ), + ( + "eval_env/route_publique_cas_par_cas.html", + "eval_env/route_publique_cas_par_cas.html", + ), + ( + "eval_env/route_publique_non_soumis.html", + "eval_env/route_publique_non_soumis.html", + ), + ( + "eval_env/route_publique_systematique.html", + "eval_env/route_publique_systematique.html", + ), + ( + "eval_env/sport_loisir_culture_cas_par_cas.html", + "eval_env/sport_loisir_culture_cas_par_cas.html", + ), + ( + "eval_env/sport_loisir_culture_non_soumis.html", + "eval_env/sport_loisir_culture_non_soumis.html", + ), + ( + "eval_env/sport_loisir_culture_non_soumis_lt1000.html", + "eval_env/sport_loisir_culture_non_soumis_lt1000.html", + ), + ( + "eval_env/surface_plancher_cas_par_cas.html", + "eval_env/surface_plancher_cas_par_cas.html", + ), + ( + "eval_env/surface_plancher_non_soumis.html", + "eval_env/surface_plancher_non_soumis.html", + ), + ( + "eval_env/terrain_assiette_cas_par_cas.html", + "eval_env/terrain_assiette_cas_par_cas.html", + ), + ( + "eval_env/terrain_assiette_non_concerne.html", + "eval_env/terrain_assiette_non_concerne.html", + ), + ( + "eval_env/terrain_assiette_non_soumis.html", + "eval_env/terrain_assiette_non_soumis.html", + ), + ( + "eval_env/terrain_assiette_systematique.html", + "eval_env/terrain_assiette_systematique.html", + ), + ( + "eval_env/voie_privee_cas_par_cas.html", + "eval_env/voie_privee_cas_par_cas.html", + ), + ( + "eval_env/voie_privee_non_soumis.html", + "eval_env/voie_privee_non_soumis.html", + ), + ( + "loi_sur_leau/autres_rubriques_non_disponible.html", + "loi_sur_leau/autres_rubriques_non_disponible.html", + ), + ( + "loi_sur_leau/ecoulement_avec_bv_action_requise.html", + "loi_sur_leau/ecoulement_avec_bv_action_requise.html", + ), + ( + "loi_sur_leau/ecoulement_avec_bv_action_requise_probable_1ha.html", + "loi_sur_leau/ecoulement_avec_bv_action_requise_probable_1ha.html", + ), + ( + "loi_sur_leau/ecoulement_avec_bv_non_soumis.html", + "loi_sur_leau/ecoulement_avec_bv_non_soumis.html", + ), + ( + "loi_sur_leau/ecoulement_avec_bv_soumis.html", + "loi_sur_leau/ecoulement_avec_bv_soumis.html", + ), + ( + "loi_sur_leau/ecoulement_sans_bv_action_requise.html", + "loi_sur_leau/ecoulement_sans_bv_action_requise.html", + ), + ( + "loi_sur_leau/ecoulement_sans_bv_non_soumis.html", + "loi_sur_leau/ecoulement_sans_bv_non_soumis.html", + ), + ( + "loi_sur_leau/ecoulement_sans_bv_soumis.html", + "loi_sur_leau/ecoulement_sans_bv_soumis.html", + ), + ( + "loi_sur_leau/result_action_requise.html", + "loi_sur_leau/result_action_requise.html", + ), + ( + "loi_sur_leau/result_non_active.html", + "loi_sur_leau/result_non_active.html", + ), + ( + "loi_sur_leau/result_non_disponible.html", + "loi_sur_leau/result_non_disponible.html", + ), + ( + "loi_sur_leau/result_non_soumis.html", + "loi_sur_leau/result_non_soumis.html", + ), + ( + "loi_sur_leau/result_soumis.html", + "loi_sur_leau/result_soumis.html", + ), + ( + "loi_sur_leau/zone_humide_action_requise.html", + "loi_sur_leau/zone_humide_action_requise.html", + ), + ( + "loi_sur_leau/zone_humide_action_requise_dans_doute.html", + "loi_sur_leau/zone_humide_action_requise_dans_doute.html", + ), + ( + "loi_sur_leau/zone_humide_action_requise_proche.html", + "loi_sur_leau/zone_humide_action_requise_proche.html", + ), + ( + "loi_sur_leau/zone_humide_action_requise_tout_dpt.html", + "loi_sur_leau/zone_humide_action_requise_tout_dpt.html", + ), + ( + "loi_sur_leau/zone_humide_non_concerne.html", + "loi_sur_leau/zone_humide_non_concerne.html", + ), + ( + "loi_sur_leau/zone_humide_non_soumis.html", + "loi_sur_leau/zone_humide_non_soumis.html", + ), + ( + "loi_sur_leau/zone_humide_soumis.html", + "loi_sur_leau/zone_humide_soumis.html", + ), + ( + "loi_sur_leau/zone_inondable_action_requise.html", + "loi_sur_leau/zone_inondable_action_requise.html", + ), + ( + "loi_sur_leau/zone_inondable_action_requise_dans_doute.html", + "loi_sur_leau/zone_inondable_action_requise_dans_doute.html", + ), + ( + "loi_sur_leau/zone_inondable_non_concerne.html", + "loi_sur_leau/zone_inondable_non_concerne.html", + ), + ( + "loi_sur_leau/zone_inondable_non_soumis.html", + "loi_sur_leau/zone_inondable_non_soumis.html", + ), + ( + "loi_sur_leau/zone_inondable_soumis.html", + "loi_sur_leau/zone_inondable_soumis.html", + ), + ( + "natura2000/autorisation_urba_a_verifier.html", + "natura2000/autorisation_urba_a_verifier.html", + ), + ( + "natura2000/autorisation_urba_non_soumis.html", + "natura2000/autorisation_urba_non_soumis.html", + ), + ( + "natura2000/autorisation_urba_non_soumis_lotissement.html", + "natura2000/autorisation_urba_non_soumis_lotissement.html", + ), + ( + "natura2000/autorisation_urba_soumis.html", + "natura2000/autorisation_urba_soumis.html", + ), + ( + "natura2000/eval_env_non_soumis.html", + "natura2000/eval_env_non_soumis.html", + ), + ( + "natura2000/eval_env_soumis_cas_par_cas.html", + "natura2000/eval_env_soumis_cas_par_cas.html", + ), + ( + "natura2000/eval_env_soumis_systematique.html", + "natura2000/eval_env_soumis_systematique.html", + ), + ( + "natura2000/iota_iota_a_verifier.html", + "natura2000/iota_iota_a_verifier.html", + ), + ( + "natura2000/iota_non_soumis.html", + "natura2000/iota_non_soumis.html", + ), + ("natura2000/iota_soumis.html", "natura2000/iota_soumis.html"), + ( + "natura2000/result_a_verifier.html", + "natura2000/result_a_verifier.html", + ), + ( + "natura2000/result_action_requise.html", + "natura2000/result_action_requise.html", + ), + ( + "natura2000/result_iota_a_verifier.html", + "natura2000/result_iota_a_verifier.html", + ), + ( + "natura2000/result_non_active.html", + "natura2000/result_non_active.html", + ), + ( + "natura2000/result_non_concerne.html", + "natura2000/result_non_concerne.html", + ), + ( + "natura2000/result_non_disponible.html", + "natura2000/result_non_disponible.html", + ), + ( + "natura2000/result_non_soumis.html", + "natura2000/result_non_soumis.html", + ), + ("natura2000/result_soumis.html", "natura2000/result_soumis.html"), + ( + "natura2000/zone_humide_action_requise_dans_doute.html", + "natura2000/zone_humide_action_requise_dans_doute.html", + ), + ( + "natura2000/zone_humide_action_requise_proche.html", + "natura2000/zone_humide_action_requise_proche.html", + ), + ( + "natura2000/zone_humide_non_concerne.html", + "natura2000/zone_humide_non_concerne.html", + ), + ( + "natura2000/zone_humide_non_soumis.html", + "natura2000/zone_humide_non_soumis.html", + ), + ( + "natura2000/zone_humide_non_soumis_dans_doute.html", + "natura2000/zone_humide_non_soumis_dans_doute.html", + ), + ( + "natura2000/zone_humide_non_soumis_proche.html", + "natura2000/zone_humide_non_soumis_proche.html", + ), + ( + "natura2000/zone_humide_soumis.html", + "natura2000/zone_humide_soumis.html", + ), + ( + "natura2000/zone_inondable_non_concerne.html", + "natura2000/zone_inondable_non_concerne.html", + ), + ( + "natura2000/zone_inondable_non_soumis.html", + "natura2000/zone_inondable_non_soumis.html", + ), + ( + "natura2000/zone_inondable_soumis.html", + "natura2000/zone_inondable_soumis.html", + ), + ( + "sage/interdiction_impact_zh_action_requise_dans_doute_interdit.html", + "sage/interdiction_impact_zh_action_requise_dans_doute_interdit.html", + ), + ( + "sage/interdiction_impact_zh_action_requise_interdit.html", + "sage/interdiction_impact_zh_action_requise_interdit.html", + ), + ( + "sage/interdiction_impact_zh_action_requise_proche_interdit.html", + "sage/interdiction_impact_zh_action_requise_proche_interdit.html", + ), + ( + "sage/interdiction_impact_zh_action_requise_tout_dpt_interdit.html", + "sage/interdiction_impact_zh_action_requise_tout_dpt_interdit.html", + ), + ( + "sage/interdiction_impact_zh_interdit.html", + "sage/interdiction_impact_zh_interdit.html", + ), + ( + "sage/interdiction_impact_zh_iota_a_verifier.html", + "sage/interdiction_impact_zh_iota_a_verifier.html", + ), + ( + "sage/interdiction_impact_zh_iota_action_requise_dans_doute_interdit.html", + "sage/interdiction_impact_zh_iota_action_requise_dans_doute_interdit.html", + ), + ( + "sage/interdiction_impact_zh_iota_action_requise_interdit.html", + "sage/interdiction_impact_zh_iota_action_requise_interdit.html", + ), + ( + "sage/interdiction_impact_zh_iota_action_requise_proche_interdit.html", + "sage/interdiction_impact_zh_iota_action_requise_proche_interdit.html", + ), + ( + "sage/interdiction_impact_zh_iota_action_requise_tout_dpt_interdit.html", + "sage/interdiction_impact_zh_iota_action_requise_tout_dpt_interdit.html", + ), + ( + "sage/interdiction_impact_zh_iota_interdit.html", + "sage/interdiction_impact_zh_iota_interdit.html", + ), + ( + "sage/interdiction_impact_zh_iota_non_soumis.html", + "sage/interdiction_impact_zh_iota_non_soumis.html", + ), + ( + "sage/interdiction_impact_zh_iota_non_soumis_dehors.html", + "sage/interdiction_impact_zh_iota_non_soumis_dehors.html", + ), + ( + "sage/interdiction_impact_zh_non_soumis.html", + "sage/interdiction_impact_zh_non_soumis.html", + ), + ( + "sage/interdiction_impact_zh_non_soumis_dehors.html", + "sage/interdiction_impact_zh_non_soumis_dehors.html", + ), + ("sage/result_a_verifier.html", "sage/result_a_verifier.html"), + ( + "sage/result_action_requise.html", + "sage/result_action_requise.html", + ), + ("sage/result_interdit.html", "sage/result_interdit.html"), + ("sage/result_non_active.html", "sage/result_non_active.html"), + ("sage/result_non_concerne.html", "sage/result_non_concerne.html"), + ( + "sage/result_non_disponible.html", + "sage/result_non_disponible.html", + ), + ("sage/result_non_soumis.html", "sage/result_non_soumis.html"), + ], + max_length=512, + verbose_name="Key", + ), + ), + migrations.CreateModel( + name="HaieDepartmentConfig", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "is_activated", + models.BooleanField( + default=False, + help_text="Le guichet unique de la haie est-il disponible pour ce département ?", + verbose_name="Is activated", + ), + ), + ( + "department_guichet_unique_url", + models.URLField( + blank=True, + verbose_name="Url du guichet unique de la haie du département (si existant)", + ), + ), + ( + "contacts_and_links", + models.TextField( + blank=True, verbose_name="Liste des contacts et liens utiles" + ), + ), + ( + "department", + models.OneToOneField( + on_delete=django.db.models.deletion.PROTECT, + related_name="haie_config", + to="geodata.department", + verbose_name="Department", + ), + ), + ], + ), + ] diff --git a/envergo/moulinette/migrations/0060_merge_20241007_1441.py b/envergo/moulinette/migrations/0060_merge_20241007_1441.py new file mode 100644 index 000000000..ceb0d9d3e --- /dev/null +++ b/envergo/moulinette/migrations/0060_merge_20241007_1441.py @@ -0,0 +1,13 @@ +# Generated by Django 4.2.13 on 2024-10-07 12:41 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("moulinette", "0059_alter_moulinettetemplate_key_haiedepartmentconfig"), + ("moulinette", "0059_n2000_zh_criterion_add_threshold"), + ] + + operations = [] diff --git a/envergo/moulinette/migrations/0061_rename_haiedepartmentconfig_confighaie_and_more.py b/envergo/moulinette/migrations/0061_rename_haiedepartmentconfig_confighaie_and_more.py new file mode 100644 index 000000000..c22428b98 --- /dev/null +++ b/envergo/moulinette/migrations/0061_rename_haiedepartmentconfig_confighaie_and_more.py @@ -0,0 +1,35 @@ +# Generated by Django 4.2.13 on 2024-10-07 12:54 + +import django.contrib.postgres.fields +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ("geodata", "0016_alter_department_geometry"), + ("moulinette", "0060_merge_20241007_1441"), + ] + + operations = [ + migrations.RenameModel( + old_name="HaieDepartmentConfig", + new_name="ConfigHaie", + ), + migrations.RenameModel( + old_name="MoulinetteConfig", + new_name="ConfigAmenagement", + ), + migrations.AlterField( + model_name="moulinettetemplate", + name="config", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name="templates", + to="moulinette.configamenagement", + verbose_name="Config", + ), + ), + ] diff --git a/envergo/moulinette/migrations/0062_alter_configamenagement_options_and_more.py b/envergo/moulinette/migrations/0062_alter_configamenagement_options_and_more.py new file mode 100644 index 000000000..c29025175 --- /dev/null +++ b/envergo/moulinette/migrations/0062_alter_configamenagement_options_and_more.py @@ -0,0 +1,58 @@ +# Generated by Django 4.2.13 on 2024-10-07 13:13 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ("geodata", "0016_alter_department_geometry"), + ("moulinette", "0061_rename_haiedepartmentconfig_confighaie_and_more"), + ] + + operations = [ + migrations.AlterModelOptions( + name="configamenagement", + options={ + "verbose_name": "Config amenagement", + "verbose_name_plural": "Configs amenagement", + }, + ), + migrations.AlterModelOptions( + name="confighaie", + options={ + "verbose_name": "Config haie", + "verbose_name_plural": "Configs haie", + }, + ), + migrations.AlterField( + model_name="configamenagement", + name="department", + field=models.OneToOneField( + on_delete=django.db.models.deletion.PROTECT, + related_name="%(class)s", + to="geodata.department", + verbose_name="Department", + ), + ), + migrations.AlterField( + model_name="confighaie", + name="department", + field=models.OneToOneField( + on_delete=django.db.models.deletion.PROTECT, + related_name="%(class)s", + to="geodata.department", + verbose_name="Department", + ), + ), + migrations.AlterField( + model_name="confighaie", + name="is_activated", + field=models.BooleanField( + default=False, + help_text="Is the moulinette available for this department?", + verbose_name="Is activated", + ), + ), + ] diff --git a/envergo/moulinette/migrations/haie_department_configs.json b/envergo/moulinette/migrations/haie_department_configs.json new file mode 100644 index 000000000..d70b84742 --- /dev/null +++ b/envergo/moulinette/migrations/haie_department_configs.json @@ -0,0 +1,53 @@ +[ + { + "model": "moulinette.ConfigHaie", + "fields": { + "department": 30, + "is_activated": true + } + }, + { + "model": "moulinette.ConfigHaie", + "fields": { + "department": 46, + "is_activated": true + } + }, + { + "model": "moulinette.ConfigHaie", + "fields": { + "department": 70, + "is_activated": true + } + }, + { + "model": "moulinette.ConfigHaie", + "fields": { + "department": 71, + "is_activated": true + } + }, + { + "model": "moulinette.ConfigHaie", + "fields": { + "department": 82, + "is_activated": true + } + }, + { + "model": "moulinette.ConfigHaie", + "fields": { + "department": 10, + "is_activated": false, + "department_guichet_unique_url": "https://www.demarches-simplifiees.fr/commencer/guichet-haies-76-demandes-d-informations-sur-la-pr" + } + }, + { + "model": "moulinette.ConfigHaie", + "fields": { + "department": 63, + "is_activated": false, + "contacts_and_links": "
\r\n DDTM de Loire-Atlantique
\r\n Unité Police de l’eau
\r\n Email : ddtm-see-guichet-unique@loire-atlantique.gouv.fr\r\n
" + } + } +] diff --git a/envergo/moulinette/models.py b/envergo/moulinette/models.py index 5925aa7c6..6110b12eb 100644 --- a/envergo/moulinette/models.py +++ b/envergo/moulinette/models.py @@ -44,6 +44,7 @@ logger = logging.getLogger(__name__) +HAIE_REGULATIONS = ["conditionnalite_pac", "dep"] # A list of required action stakes. # For example, a user might learn that an action is required, to check if the @@ -657,20 +658,32 @@ def contact(self): return mark_safe(contact) -class MoulinetteConfig(models.Model): - """Some moulinette content depends on the department.""" - +class ConfigBase(models.Model): department = models.OneToOneField( "geodata.Department", verbose_name=_("Department"), on_delete=models.PROTECT, - related_name="moulinette_config", + related_name="%(class)s", ) is_activated = models.BooleanField( _("Is activated"), help_text=_("Is the moulinette available for this department?"), default=False, ) + + class Meta: + abstract = True + + def __str__(self): + return self.department.get_department_display() + + +class ConfigAmenagement(ConfigBase): + """Some moulinette content depends on the department. + + This object is dedicated to the Amenagement moulinette. For Haie, see ConfigHaie. + """ + regulations_available = ArrayField( base_field=models.CharField(max_length=64, choices=REGULATIONS), blank=True, @@ -702,12 +715,33 @@ class MoulinetteConfig(models.Model): ) class Meta: - verbose_name = _("Moulinette config") - verbose_name_plural = _("Moulinette configs") + verbose_name = _("Config amenagement") + verbose_name_plural = _("Configs amenagement") + + +class ConfigHaie(ConfigBase): + """Some moulinette content depends on the department. + + This object is dedicated to the Haie moulinette. For Amenagement, see ConfigAmenagement. + """ + + regulations_available = HAIE_REGULATIONS + + department_guichet_unique_url = models.URLField( + "Url du guichet unique de la haie du département (si existant)", blank=True + ) + + contacts_and_links = models.TextField( + "Liste des contacts et liens utiles", blank=True + ) def __str__(self): return self.department.get_department_display() + class Meta: + verbose_name = "Config haie" + verbose_name_plural = "Configs haie" + TEMPLATE_KEYS = [ "autorisation_urba_pa", @@ -728,12 +762,12 @@ def get_all_template_keys(): class MoulinetteTemplate(models.Model): """A custom moulinette template that can be admin edited. - Templates can be associated to departments (through MoulinetteConfig) or + Templates can be associated to departments (through ConfigAmenagement) or criteria. """ config = models.ForeignKey( - "moulinette.MoulinetteConfig", + "moulinette.ConfigAmenagement", verbose_name=_("Config"), on_delete=models.PROTECT, related_name="templates", @@ -822,10 +856,10 @@ def __init__(self, data, raw_data, activate_optional_criteria=True): # Some criteria must be hidden to normal users in the self.activate_optional_criteria = activate_optional_criteria - self.load_specific_data() + self.department = self.get_department() self.config = self.catalog["config"] = self.get_config() - if self.config and self.config.id: + if self.config and self.config.id and hasattr(self.config, "templates"): self.templates = {t.key: t for t in self.config.templates.all()} else: self.templates = {} @@ -846,11 +880,6 @@ def evaluate(self): for regulation in self.regulations: regulation.evaluate(self) - @abstractmethod - def load_specific_data(self): - """Load data specific for a given moulinette instance.""" - pass - def has_config(self): return bool(self.config) @@ -877,6 +906,20 @@ def get_debug_result_template(self): raise AttributeError("No result template found.") return self.debug_result_template + def get_result_non_disponible_template(self): + """Return the template to display the result_non_disponible page.""" + + if not hasattr(self, "result_non_disponible"): + raise AttributeError("No result_non_disponible template found.") + return self.result_non_disponible + + def get_result_available_soon_template(self): + """Return the template to display the result_available_soon page.""" + + if not hasattr(self, "result_available_soon"): + raise AttributeError("No result_available_soon template found.") + return self.result_available_soon + @classmethod def get_main_form_class(cls): """Return the form class for the main questions.""" @@ -1153,6 +1196,8 @@ class MoulinetteAmenagement(Moulinette): REGULATIONS = ["loi_sur_leau", "natura2000", "eval_env", "sage"] result_template = "amenagement/moulinette/result.html" debug_result_template = "amenagement/moulinette/result_debug.html" + result_available_soon = "amenagement/moulinette/result_available_soon.html" + result_non_disponible = "amenagement/moulinette/result_non_disponible.html" form_template = "amenagement/moulinette/form.html" main_form_class = MoulinetteFormAmenagement @@ -1330,21 +1375,18 @@ def summary(self): return summary - def load_specific_data(self): - self.department = self.get_department() - def get_department(self): lng_lat = self.catalog["lng_lat"] department = ( Department.objects.filter(geometry__contains=lng_lat) - .select_related("moulinette_config") - .prefetch_related("moulinette_config__templates") + .select_related("configamenagement") + .prefetch_related("configamenagement__templates") .first() ) return department def get_config(self): - return getattr(self.department, "moulinette_config", None) + return getattr(self.department, "configamenagement", None) def get_debug_context(self): # In the debug page, we want to factorize the maps we display, so we order them @@ -1377,16 +1419,16 @@ def get_triage_params(cls): class MoulinetteHaie(Moulinette): - REGULATIONS = ["conditionnalite_pac", "dep"] + REGULATIONS = HAIE_REGULATIONS result_template = "haie/moulinette/result.html" debug_result_template = "haie/moulinette/result.html" + result_available_soon = "haie/moulinette/result_non_disponible.html" + result_non_disponible = "haie/moulinette/result_non_disponible.html" form_template = "haie/moulinette/form.html" main_form_class = MoulinetteFormHaie def get_config(self): - return MoulinetteConfig( - is_activated=True, regulations_available=self.REGULATIONS - ) + return getattr(self.department, "confighaie", None) def summary(self): """Build a data summary, for analytics purpose.""" @@ -1401,10 +1443,6 @@ def summary(self): return summary - def load_specific_data(self): - """There is no specific needs for the Haie moulinette.""" - pass - def get_debug_context(self): return {} @@ -1425,8 +1463,36 @@ def get_extra_context(cls, request): context["triage_form"] = triage_form else: context["redirect_url"] = context["triage_url"] + + department_code = request.GET.get("department", None) + department = ( + ( + Department.objects.defer("geometry") + .filter(confighaie__is_activated=True, department=department_code) + .first() + ) + if department_code + else None + ) + context["department"] = department + return context + def get_department(self): + department_code = self.raw_data.get("department", None) + department = ( + ( + Department.objects.defer("geometry") + .select_related("confighaie") + .filter(department=department_code) + .first() + ) + if department_code + else None + ) + + return department + def get_moulinette_class_from_site(site): """Return the correct Moulinette class depending on the current site.""" diff --git a/envergo/moulinette/templatetags/moulinette.py b/envergo/moulinette/templatetags/moulinette.py index 5ffc1edc2..98f96e11c 100644 --- a/envergo/moulinette/templatetags/moulinette.py +++ b/envergo/moulinette/templatetags/moulinette.py @@ -84,7 +84,7 @@ def show_criterion_body(context, regulation, criterion): def criterion_value(config, criterion, field): """Display a criterion static value. - If this value is overriden in the MoulinetteConfig instance, + If this value is overriden in the ConfigAmenagement or ConfigHaie instance, display the config value instead. """ values = config.criteria_values diff --git a/envergo/moulinette/tests/factories.py b/envergo/moulinette/tests/factories.py index 8d10b908e..444b64128 100644 --- a/envergo/moulinette/tests/factories.py +++ b/envergo/moulinette/tests/factories.py @@ -2,12 +2,18 @@ from factory.django import DjangoModelFactory from envergo.geodata.tests.factories import DepartmentFactory, MapFactory -from envergo.moulinette.models import Criterion, MoulinetteConfig, Perimeter, Regulation +from envergo.moulinette.models import ( + ConfigAmenagement, + ConfigHaie, + Criterion, + Perimeter, + Regulation, +) -class MoulinetteConfigFactory(DjangoModelFactory): +class ConfigAmenagementFactory(DjangoModelFactory): class Meta: - model = MoulinetteConfig + model = ConfigAmenagement department = factory.SubFactory(DepartmentFactory) is_activated = True @@ -40,3 +46,11 @@ class Meta: activation_map = factory.SubFactory(MapFactory) regulation = factory.SubFactory(RegulationFactory) is_activated = True + + +class ConfigHaieFactory(DjangoModelFactory): + class Meta: + model = ConfigHaie + + department = factory.SubFactory(DepartmentFactory) + is_activated = True diff --git a/envergo/moulinette/tests/test_conditionnalite_pac.py b/envergo/moulinette/tests/test_conditionnalite_pac.py index 9d06f80d3..4acb6743b 100644 --- a/envergo/moulinette/tests/test_conditionnalite_pac.py +++ b/envergo/moulinette/tests/test_conditionnalite_pac.py @@ -2,7 +2,11 @@ from envergo.geodata.conftest import france_map # noqa from envergo.moulinette.models import MoulinetteHaie -from envergo.moulinette.tests.factories import CriterionFactory, RegulationFactory +from envergo.moulinette.tests.factories import ( + ConfigHaieFactory, + CriterionFactory, + RegulationFactory, +) pytestmark = pytest.mark.django_db @@ -22,10 +26,12 @@ def conditionnalite_pac_criteria(france_map): # noqa def test_conditionnalite_pac_only_for_agri_pac(): + ConfigHaieFactory() data = { "profil": "autre", "motif": "chemin_acces", "reimplantation": "remplacement", + "department": "44", } for motif_choice in [ "transfert_parcelles", @@ -46,10 +52,12 @@ def test_conditionnalite_pac_only_for_agri_pac(): def test_conditionnalite_pac_for_agri_pac(): + ConfigHaieFactory() data = { "profil": "agri_pac", "motif": "chemin_acces", "reimplantation": "remplacement", + "department": "44", } moulinette = MoulinetteHaie(data, data, False) diff --git a/envergo/moulinette/tests/test_dep.py b/envergo/moulinette/tests/test_dep.py index 9badbf548..6f4ecf259 100644 --- a/envergo/moulinette/tests/test_dep.py +++ b/envergo/moulinette/tests/test_dep.py @@ -2,7 +2,11 @@ from envergo.geodata.conftest import france_map # noqa from envergo.moulinette.models import MoulinetteHaie -from envergo.moulinette.tests.factories import CriterionFactory, RegulationFactory +from envergo.moulinette.tests.factories import ( + ConfigHaieFactory, + CriterionFactory, + RegulationFactory, +) pytestmark = pytest.mark.django_db @@ -22,10 +26,12 @@ def dep_criteria(france_map): # noqa def test_dep_is_soumis(): + ConfigHaieFactory() data = { "profil": "autre", "motif": "chemin_acces", "reimplantation": "remplacement", + "department": "44", } for motif_choice in [ "transfert_parcelles", diff --git a/envergo/moulinette/tests/test_evalenv.py b/envergo/moulinette/tests/test_evalenv.py index 81ff3b9ff..97b3b16bc 100644 --- a/envergo/moulinette/tests/test_evalenv.py +++ b/envergo/moulinette/tests/test_evalenv.py @@ -5,8 +5,8 @@ from envergo.geodata.conftest import france_map # noqa from envergo.moulinette.models import MoulinetteAmenagement from envergo.moulinette.tests.factories import ( + ConfigAmenagementFactory, CriterionFactory, - MoulinetteConfigFactory, RegulationFactory, ) @@ -236,7 +236,7 @@ def test_evalenv_terrain_assiette_systematique(moulinette_data): def test_evalenv_non_soumis_no_optional_criteria(admin_client): """When no optional form is activated, we can show the result.""" - MoulinetteConfigFactory() + ConfigAmenagementFactory() url = reverse("moulinette_result") params = "created_surface=500&final_surface=500&lng=-1.54394&lat=47.21381" @@ -261,7 +261,7 @@ def test_evalenv_non_soumis_no_optional_criteria(admin_client): def test_evalenv_non_soumis_missing_optional_criteria(admin_client): """When optional data is missing, we don't show the result page.""" - MoulinetteConfigFactory() + ConfigAmenagementFactory() url = reverse("moulinette_result") params = ( @@ -276,7 +276,7 @@ def test_evalenv_non_soumis_missing_optional_criteria(admin_client): def test_evalenv_non_soumis_optional_criteria(admin_client): - MoulinetteConfigFactory() + ConfigAmenagementFactory() url = reverse("moulinette_result") params = ( @@ -302,7 +302,7 @@ def test_evalenv_non_soumis_optional_criteria(admin_client): def test_evalenv_rubrique44(admin_client): - MoulinetteConfigFactory() + ConfigAmenagementFactory() # Type d'equipement concerné et capacité d'accueil >= 1000 => Cas par cas url = reverse("moulinette_result") diff --git a/envergo/moulinette/tests/test_loisurleau.py b/envergo/moulinette/tests/test_loisurleau.py index ceafc383a..6f65d70b1 100644 --- a/envergo/moulinette/tests/test_loisurleau.py +++ b/envergo/moulinette/tests/test_loisurleau.py @@ -3,8 +3,8 @@ from envergo.geodata.conftest import france_map # noqa from envergo.moulinette.models import MoulinetteAmenagement from envergo.moulinette.tests.factories import ( + ConfigAmenagementFactory, CriterionFactory, - MoulinetteConfigFactory, RegulationFactory, ) @@ -174,7 +174,7 @@ def test_3310_large_footprint_outside_wetlands(moulinette_data): def test_3310_large_footprint_inside_doubt_department(moulinette_data): """Project with footprint > 1000m² inside a whole zh department.""" - MoulinetteConfigFactory(zh_doubt=True) + ConfigAmenagementFactory(zh_doubt=True) moulinette = MoulinetteAmenagement(moulinette_data, moulinette_data) moulinette.catalog["within_potential_wetlands_deprartment"] = True moulinette.evaluate() diff --git a/envergo/moulinette/tests/test_models.py b/envergo/moulinette/tests/test_models.py index b88e0aca4..e10ef774c 100644 --- a/envergo/moulinette/tests/test_models.py +++ b/envergo/moulinette/tests/test_models.py @@ -12,8 +12,9 @@ get_moulinette_class_from_url, ) from envergo.moulinette.tests.factories import ( + ConfigAmenagementFactory, + ConfigHaieFactory, CriterionFactory, - MoulinetteConfigFactory, PerimeterFactory, RegulationFactory, ) @@ -82,7 +83,7 @@ def test_moulinette_config(moulinette_data): moulinette = MoulinetteAmenagement(moulinette_data, moulinette_data) assert not moulinette.has_config() - MoulinetteConfigFactory(is_activated=False) + ConfigAmenagementFactory(is_activated=False) moulinette = MoulinetteAmenagement(moulinette_data, moulinette_data) assert moulinette.has_config() @@ -91,7 +92,7 @@ def test_moulinette_config(moulinette_data): def test_result_with_inactive_contact_data(moulinette_data): """Dept contact info is not activated, we cannot run the eval.""" - MoulinetteConfigFactory(is_activated=False) + ConfigAmenagementFactory(is_activated=False) moulinette = MoulinetteAmenagement(moulinette_data, moulinette_data) assert not moulinette.is_evaluation_available() @@ -100,7 +101,7 @@ def test_result_with_inactive_contact_data(moulinette_data): def test_result_with_contact_data(moulinette_data): """Dept contact info is set, we can run the eval.""" - MoulinetteConfigFactory(is_activated=True) + ConfigAmenagementFactory(is_activated=True) moulinette = MoulinetteAmenagement(moulinette_data, moulinette_data) assert moulinette.is_evaluation_available() @@ -108,7 +109,7 @@ def test_result_with_contact_data(moulinette_data): @pytest.mark.parametrize("footprint", [50]) def test_moulinette_amenagement_has_specific_behavior(moulinette_data): site = SiteFactory() - MoulinetteConfigFactory(is_activated=True) + ConfigAmenagementFactory(is_activated=True) MoulinetteClass = get_moulinette_class_from_site(site) moulinette = MoulinetteClass(moulinette_data, moulinette_data) assert moulinette.is_evaluation_available() @@ -121,10 +122,11 @@ def test_moulinette_amenagement_has_specific_behavior(moulinette_data): def test_moulinette_haie_has_specific_behavior(): + ConfigHaieFactory() site = SiteFactory() site.domain = "haie.beta.gouv.fr" MoulinetteClass = get_moulinette_class_from_site(site) - moulinette = MoulinetteClass({}, {}) + moulinette = MoulinetteClass({}, {"department": "44"}) assert moulinette.is_evaluation_available() assert moulinette.get_main_form_class() == MoulinetteFormHaie assert moulinette.get_form_template() == "haie/moulinette/form.html" diff --git a/envergo/moulinette/tests/test_n2000_evalenv.py b/envergo/moulinette/tests/test_n2000_evalenv.py index bc65669a5..a3f176d26 100644 --- a/envergo/moulinette/tests/test_n2000_evalenv.py +++ b/envergo/moulinette/tests/test_n2000_evalenv.py @@ -3,8 +3,8 @@ from envergo.geodata.conftest import france_map # noqa from envergo.moulinette.models import MoulinetteAmenagement from envergo.moulinette.tests.factories import ( + ConfigAmenagementFactory, CriterionFactory, - MoulinetteConfigFactory, RegulationFactory, ) @@ -18,7 +18,7 @@ def autouse_site(site): @pytest.fixture(autouse=True) def evalenv_criteria(france_map): # noqa - MoulinetteConfigFactory( + ConfigAmenagementFactory( is_activated=True, ddtm_water_police_email="ddtm_email_test@example.org", ) diff --git a/envergo/moulinette/tests/test_n2000_iota.py b/envergo/moulinette/tests/test_n2000_iota.py index 1d3c40414..5360a9e5e 100644 --- a/envergo/moulinette/tests/test_n2000_iota.py +++ b/envergo/moulinette/tests/test_n2000_iota.py @@ -3,8 +3,8 @@ from envergo.geodata.conftest import france_map # noqa from envergo.moulinette.models import MoulinetteAmenagement from envergo.moulinette.tests.factories import ( + ConfigAmenagementFactory, CriterionFactory, - MoulinetteConfigFactory, RegulationFactory, ) @@ -18,7 +18,7 @@ def autouse_site(site): @pytest.fixture(autouse=True) def loisurleau_criteria(france_map): # noqa - MoulinetteConfigFactory( + ConfigAmenagementFactory( is_activated=True, ddtm_water_police_email="ddtm_email_test@example.org", ) diff --git a/envergo/moulinette/tests/test_optional_criteria.py b/envergo/moulinette/tests/test_optional_criteria.py index fc801584e..7edc4665e 100644 --- a/envergo/moulinette/tests/test_optional_criteria.py +++ b/envergo/moulinette/tests/test_optional_criteria.py @@ -4,8 +4,8 @@ from envergo.geodata.conftest import france_map # noqa from envergo.moulinette.tests.factories import ( + ConfigAmenagementFactory, CriterionFactory, - MoulinetteConfigFactory, RegulationFactory, ) @@ -19,7 +19,7 @@ def autouse_site(site): @pytest.fixture(autouse=True) def evalenv_criteria(france_map): # noqa - _config = MoulinetteConfigFactory(is_activated=True) # noqa + _config = ConfigAmenagementFactory(is_activated=True) # noqa regulation = RegulationFactory(regulation="eval_env") criteria = [ CriterionFactory( diff --git a/envergo/moulinette/tests/test_sage.py b/envergo/moulinette/tests/test_sage.py index 02463f8a1..aeb1459ce 100644 --- a/envergo/moulinette/tests/test_sage.py +++ b/envergo/moulinette/tests/test_sage.py @@ -6,8 +6,8 @@ from envergo.geodata.conftest import france_map # noqa from envergo.moulinette.models import Criterion, MoulinetteAmenagement from envergo.moulinette.tests.factories import ( + ConfigAmenagementFactory, CriterionFactory, - MoulinetteConfigFactory, Perimeter, PerimeterFactory, RegulationFactory, @@ -55,7 +55,7 @@ def moulinette_data(footprint): def test_result_interdit(moulinette_data): """Test the default criterion result""" - MoulinetteConfigFactory(is_activated=True) + ConfigAmenagementFactory(is_activated=True) moulinette = MoulinetteAmenagement(moulinette_data, moulinette_data) moulinette.catalog["forbidden_wetlands_within_25m"] = True moulinette.evaluate() @@ -67,7 +67,7 @@ def test_result_interdit(moulinette_data): def test_deactivated_regulation(moulinette_data): """Test single regulation deactivation in moulinette config.""" - MoulinetteConfigFactory(is_activated=True, regulations_available=[]) + ConfigAmenagementFactory(is_activated=True, regulations_available=[]) moulinette = MoulinetteAmenagement(moulinette_data, moulinette_data) moulinette.catalog["forbidden_wetlands_within_25m"] = True moulinette.evaluate() @@ -79,7 +79,7 @@ def test_deactivated_regulation(moulinette_data): def test_default_result_when_a_perimeter_is_found(moulinette_data): Criterion.objects.all().delete() - MoulinetteConfigFactory(is_activated=True) + ConfigAmenagementFactory(is_activated=True) moulinette = MoulinetteAmenagement(moulinette_data, moulinette_data) moulinette.catalog["forbidden_wetlands_within_25m"] = True moulinette.evaluate() @@ -92,7 +92,7 @@ def test_default_result_when_a_perimeter_is_found(moulinette_data): def test_default_result_when_a_perimeter_is_deactivated(moulinette_data): Criterion.objects.all().delete() - MoulinetteConfigFactory(is_activated=True) + ConfigAmenagementFactory(is_activated=True) moulinette = MoulinetteAmenagement(moulinette_data, moulinette_data) moulinette.catalog["forbidden_wetlands_within_25m"] = True @@ -109,7 +109,7 @@ def test_default_result_when_a_perimeter_is_not_found(moulinette_data): Criterion.objects.all().delete() Perimeter.objects.all().delete() - MoulinetteConfigFactory(is_activated=True) + ConfigAmenagementFactory(is_activated=True) moulinette = MoulinetteAmenagement(moulinette_data, moulinette_data) moulinette.catalog["forbidden_wetlands_within_25m"] = True moulinette.evaluate() @@ -122,7 +122,7 @@ def test_default_result_when_a_perimeter_is_not_found(moulinette_data): def test_perimeter_map_display(moulinette_data, client): """The perimeter map should be displayed in the result page.""" - MoulinetteConfigFactory(is_activated=True) + ConfigAmenagementFactory(is_activated=True) url = reverse("moulinette_result") params = urlencode(moulinette_data) @@ -143,7 +143,7 @@ def test_several_perimeter_maps_display( ): """When several perimeters are found, they are all displayed.""" - MoulinetteConfigFactory(is_activated=True) + ConfigAmenagementFactory(is_activated=True) PerimeterFactory( name="Sage Test", activation_map=france_map, diff --git a/envergo/moulinette/tests/test_views.py b/envergo/moulinette/tests/test_views.py index 4bcd57dd5..0075d2cc7 100644 --- a/envergo/moulinette/tests/test_views.py +++ b/envergo/moulinette/tests/test_views.py @@ -2,7 +2,7 @@ from django.urls import reverse from pytest_django.asserts import assertTemplateUsed -from envergo.moulinette.tests.factories import MoulinetteConfigFactory +from envergo.moulinette.tests.factories import ConfigAmenagementFactory pytestmark = pytest.mark.django_db @@ -49,7 +49,7 @@ def test_moulinette_result_without_config(client): res = client.get(full_url) assert res.status_code == 200 - assertTemplateUsed(res, "moulinette/result_non_disponible.html") + assertTemplateUsed(res, "amenagement/moulinette/result_non_disponible.html") def test_moulinette_result_without_config_admin_access(client, admin_user): @@ -62,11 +62,11 @@ def test_moulinette_result_without_config_admin_access(client, admin_user): res = client.get(full_url) assert res.status_code == 200 - assertTemplateUsed(res, "moulinette/result_non_disponible.html") + assertTemplateUsed(res, "amenagement/moulinette/result_non_disponible.html") def test_moulinette_result_with_deactivated_config(client): - MoulinetteConfigFactory(is_activated=False) + ConfigAmenagementFactory(is_activated=False) url = reverse("moulinette_result") params = "created_surface=500&final_surface=500&lng=-1.54394&lat=47.21381" @@ -78,7 +78,7 @@ def test_moulinette_result_with_deactivated_config(client): def test_moulinette_result_with_deactivated_config_admin_access(client, admin_user): - MoulinetteConfigFactory(is_activated=False) + ConfigAmenagementFactory(is_activated=False) client.force_login(admin_user) url = reverse("moulinette_result") @@ -92,7 +92,7 @@ def test_moulinette_result_with_deactivated_config_admin_access(client, admin_us def test_moulinette_result_with_activated_config(client): - MoulinetteConfigFactory(is_activated=True) + ConfigAmenagementFactory(is_activated=True) url = reverse("moulinette_result") params = "created_surface=500&final_surface=500&lng=-1.54394&lat=47.21381" @@ -113,7 +113,7 @@ def test_moulinette_result_without_params_redirects_to_home(client): def test_moulinette_result_form_error(client): """Bad params are cleaned from the result url.""" - MoulinetteConfigFactory() + ConfigAmenagementFactory() url = reverse("moulinette_result") params = ( @@ -131,7 +131,7 @@ def test_moulinette_result_form_error(client): def test_moulinette_result_mtm_keywords_are_not_bad_params(client): """Analytics params are not cleaned from the result url.""" - MoulinetteConfigFactory(is_activated=True) + ConfigAmenagementFactory(is_activated=True) url = reverse("moulinette_result") params = "created_surface=500&final_surface=500&lng=-1.54394&lat=47.21381&mtm_campaign=test" @@ -143,7 +143,7 @@ def test_moulinette_result_mtm_keywords_are_not_bad_params(client): def test_moulinette_result_custom_matomo_tracking_url(client): - MoulinetteConfigFactory(is_activated=True) + ConfigAmenagementFactory(is_activated=True) url = reverse("moulinette_result") params = "created_surface=500&final_surface=500&lng=-1.54394&lat=47.21381&mtm_campaign=test" diff --git a/envergo/moulinette/urls_haie.py b/envergo/moulinette/urls_haie.py index ba11db094..759e90fd8 100644 --- a/envergo/moulinette/urls_haie.py +++ b/envergo/moulinette/urls_haie.py @@ -1,7 +1,8 @@ -from django.urls import include, path -from django.utils.translation import gettext_lazy as _ +from django.urls import path -from envergo.moulinette.views import Triage, TriageResult +from envergo.moulinette.views import Triage + +from .urls import urlpatterns as common_urlpatterns urlpatterns = [ path( @@ -9,10 +10,4 @@ Triage.as_view(), name="triage", ), - path( - _("result/"), - TriageResult.as_view(), - name="triage_result", - ), - path(_("moulinette/"), include("envergo.moulinette.urls")), -] +] + common_urlpatterns diff --git a/envergo/moulinette/views.py b/envergo/moulinette/views.py index bbc746fbe..021a0beca 100644 --- a/envergo/moulinette/views.py +++ b/envergo/moulinette/views.py @@ -5,11 +5,12 @@ from django.conf import settings from django.http import HttpResponseRedirect, QueryDict from django.urls import reverse -from django.views.generic import FormView, TemplateView +from django.views.generic import FormView from envergo.analytics.forms import FeedbackFormUseful, FeedbackFormUseless from envergo.analytics.utils import is_request_from_a_bot, log_event from envergo.evaluations.models import RESULTS +from envergo.geodata.models import Department from envergo.geodata.utils import get_address_from_coords from envergo.moulinette.forms import TriageFormHaie from envergo.moulinette.models import get_moulinette_class_from_site @@ -144,6 +145,7 @@ def render_to_response(self, context, **response_kwargs): # We have to store the moulinette since there are no other way # to give parameters to `get_template_names` self.moulinette = context.get("moulinette", None) + self.triage_form = context.get("triage_form", None) return super().render_to_response(context, **response_kwargs) def get_additional_forms(self, moulinette): @@ -216,9 +218,6 @@ def get_all_optional_form_classes(self): return form_classes - def form_valid(self, form): - return HttpResponseRedirect(self.get_results_url(form)) - def get_results_url(self, form): """Generates the GET url corresponding to the POST'ed moulinette query. @@ -308,6 +307,9 @@ def get(self, request, *args, **kwargs): else: return res + def form_valid(self, form): + return HttpResponseRedirect(self.get_results_url(form)) + class MoulinetteResult(MoulinetteMixin, FormView): event_category = "simulateur" @@ -317,20 +319,23 @@ def get_template_names(self): """Check which template to use depending on the moulinette result.""" moulinette = self.moulinette + triage_form = self.triage_form is_debug = bool(self.request.GET.get("debug", False)) is_edit = bool(self.request.GET.get("edit", False)) is_admin = self.request.user.is_staff - if moulinette is None: + if moulinette is None and triage_form is None: template_name = "moulinette/home.html" + elif moulinette is None: + template_name = "haie/moulinette/triage_result.html" elif is_debug: template_name = moulinette.get_debug_result_template() elif is_edit: template_name = "moulinette/home.html" elif not moulinette.has_config(): - template_name = "moulinette/result_non_disponible.html" + template_name = moulinette.get_result_non_disponible_template() elif not (moulinette.is_evaluation_available() or is_admin): - template_name = "moulinette/result_available_soon.html" + template_name = moulinette.get_result_available_soon_template() elif moulinette.has_missing_data(): template_name = "moulinette/home.html" else: @@ -343,6 +348,7 @@ def get(self, request, *args, **kwargs): context = self.get_context_data() res = self.render_to_response(context) moulinette = self.moulinette + triage_form = self.triage_form if "redirect_url" in context: return HttpResponseRedirect(context["redirect_url"]) @@ -361,6 +367,8 @@ def get(self, request, *args, **kwargs): ): self.log_moulinette_event(moulinette) + return res + elif triage_form is not None: return res else: return HttpResponseRedirect(reverse("moulinette_home")) @@ -384,6 +392,8 @@ def validate_results_url(self, request, context): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) + + moulinette = context.get("moulinette", None) # Let's build custom uris for better matomo tracking # Depending on the moulinette result, we want to track different uris # as if they were distinct pages. @@ -392,7 +402,11 @@ def get_context_data(self, **kwargs): share_print_url = update_qs(current_url, {"mtm_campaign": "print-simu"}) debug_result_url = update_qs(current_url, {"debug": "true"}) result_url = remove_from_qs(current_url, "debug") - edit_url = update_qs(result_url, {"edit": "true"}) + edit_url = ( + update_qs(result_url, {"edit": "true"}) + if moulinette + else context.get("triage_url", None) + ) # Url without any query parameters # We want to build "fake" urls for matomo tracking # For example, if the current url is /simulateur/resultat/?debug=true, @@ -417,8 +431,6 @@ def get_context_data(self, **kwargs): context["share_print_url"] = share_print_url context["envergo_url"] = self.request.build_absolute_uri("/") context["base_result"] = "moulinette/base_result.html" - - moulinette = context.get("moulinette", None) is_debug = bool(self.request.GET.get("debug", False)) is_edit = bool(self.request.GET.get("edit", False)) @@ -468,6 +480,29 @@ class Triage(FormView): form_class = TriageFormHaie template_name = "haie/moulinette/triage.html" + def get(self, request, *args, **kwargs): + """This page should always have a department to be displayed.""" + context = self.get_context_data() + if not context.get("department", None): + return HttpResponseRedirect(reverse("home")) + return self.render_to_response(self.get_context_data()) + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + department_code = self.request.GET.get("department", None) + department = ( + ( + Department.objects.defer("geometry") + .filter(department=department_code) + .first() + ) + if department_code + else None + ) + context["department"] = department + + return context + def get_initial(self): """Populate the form with data from the query string.""" return self.request.GET.dict() @@ -477,36 +512,8 @@ def form_valid(self, form): if query_params["element"] == "haie" and query_params["travaux"] == "arrachage": url = reverse("moulinette_home") else: - url = reverse("triage_result") + url = reverse("moulinette_result") query_string = urlencode(query_params) url_with_params = f"{url}?{query_string}" return HttpResponseRedirect(url_with_params) - - -class TriageResult(TemplateView): - template_name = "haie/moulinette/triage_result.html" - - def get(self, request, *args, **kwargs): - context = self.get_context_data(**kwargs) - form = context["form"] - if not form.is_valid(): - return HttpResponseRedirect(reverse("triage")) - return self.render_to_response(context) - - def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - form_data = self.request.GET - context["form"] = TriageFormHaie(data=form_data) - - envergo_url = self.request.build_absolute_uri("/") - current_url = self.request.build_absolute_uri() - share_print_url = update_qs(current_url, {"mtm_campaign": "print-simu"}) - edit_url = update_qs(reverse("triage"), form_data) - - context["current_url"] = current_url - context["share_print_url"] = share_print_url - context["envergo_url"] = envergo_url - context["edit_url"] = edit_url - - return context diff --git a/envergo/pages/templatetags/pages.py b/envergo/pages/templatetags/pages.py index 5d15d8da5..747ce1f05 100644 --- a/envergo/pages/templatetags/pages.py +++ b/envergo/pages/templatetags/pages.py @@ -1,16 +1,19 @@ import random +from typing import Literal +from urllib.parse import urlencode from django import template +from django.core.cache import cache from django.urls import reverse from django.utils.safestring import mark_safe -from envergo.moulinette.models import MoulinetteConfig +from envergo.geodata.models import Department +from envergo.moulinette.models import ConfigAmenagement, ConfigHaie register = template.Library() -def nav_link(route, label, *event_data, aria_current=False): - url = reverse(route) +def nav_link(url, label, *event_data, aria_current=False): aria_current = 'aria-current="page"' if aria_current else "" data_attrs = "" @@ -41,7 +44,7 @@ def menu_item(context, route, label, *event_data, subroutes=[]): current_route = "" aria_current = route == current_route or current_route in subroutes - return nav_link(route, label, *event_data, aria_current=aria_current) + return nav_link(reverse(route), label, *event_data, aria_current=aria_current) @register.simple_tag(takes_context=True) @@ -99,8 +102,8 @@ def faq_menu(context): def evaluation_menu(context): """Generate html for the "Mes avis réglementaires" collapsible menu.""" links = ( - ("evaluation_search", "Retrouver un avis", []), - ("dashboard", "Tableau de bord", []), + (reverse("evaluation_search"), "Retrouver un avis", []), + (reverse("dashboard"), "Tableau de bord", []), ) # Other urls that can be reached from the menu @@ -116,7 +119,7 @@ def project_owner_menu(context, is_slim=False): """Generate html for the "Equipes projet" collapsible menu.""" links = ( ( - "geometricians", + reverse("geometricians"), "Géomètres-experts", ["GeometrePage", "SimulationClick", "Nav"], ), @@ -129,13 +132,27 @@ def project_owner_menu(context, is_slim=False): @register.simple_tag(takes_context=True) def pilote_departments_menu(context, is_slim=False): - """Generate html for the "Equipes projet" collapsible menu.""" + """Generate html for the "Départements pilotes" collapsible menu.""" + cache_key = "activated_departments" + activated_departments = cache.get(cache_key) + + if not activated_departments: + activated_departments = ( + Department.objects.defer("geometry") + .filter(confighaie__is_activated=True) + .all() + ) + cache.set( + cache_key, activated_departments, timeout=60 * 15 + ) # Cache for 15 minutes + links = ( ( - "triage", - "Indre", + f"{reverse('triage')}?{urlencode({'department': department.department})}", + department, [], - ), + ) + for department in activated_departments ) return collapsible_menu( @@ -188,10 +205,10 @@ def collapsible_menu( @register.simple_tag() -def nb_available_depts(): +def nb_available_depts(site: Literal["haie", "amenagement"] = "amenagement"): """Return nb of depts where EnvErgo is available.""" - - return MoulinetteConfig.objects.filter(is_activated=True).count() + Config = {"haie": ConfigHaie, "amenagement": ConfigAmenagement}.get(site) + return Config.objects.filter(is_activated=True).count() @register.simple_tag(takes_context=True) diff --git a/envergo/pages/views.py b/envergo/pages/views.py index 6726de2da..ef55fb40e 100644 --- a/envergo/pages/views.py +++ b/envergo/pages/views.py @@ -1,16 +1,19 @@ from datetime import date, timedelta +from urllib.parse import urlencode import requests from django.conf import settings from django.contrib import messages from django.contrib.syndication.views import Feed +from django.http import HttpResponseRedirect from django.urls import reverse from django.utils.formats import date_format from django.utils.html import mark_safe from django.views.generic import FormView, ListView, TemplateView from config.settings.base import GEOMETRICIAN_WEBINAR_FORM_URL -from envergo.moulinette.models import MoulinetteConfig +from envergo.geodata.models import Department +from envergo.moulinette.models import ConfigAmenagement from envergo.moulinette.views import MoulinetteMixin from envergo.pages.models import NewsItem @@ -19,9 +22,52 @@ class HomeAmenagementView(MoulinetteMixin, FormView): template_name = "amenagement/pages/home.html" -class HomeHaieView(MoulinetteMixin, FormView): +class HomeHaieView(TemplateView): template_name = "haie/pages/home.html" + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + departments = ( + Department.objects.defer("geometry").select_related("confighaie").all() + ) + context["departments"] = departments + context["activated_departments"] = [ + department + for department in departments + if department + and hasattr(department, "confighaie") + and department.confighaie.is_activated + ] + return context + + def post(self, request, *args, **kwargs): + data = request.POST + department_id = data.get("department") + department = None + if department_id: + department = ( + Department.objects.select_related("confighaie") + .defer("geometry") + .get(id=department_id) + ) + + config = ( + department.confighaie + if department and hasattr(department, "confighaie") + else None + ) + + if config and config.is_activated: + query_params = {"department": department.department} + return HttpResponseRedirect( + f"{reverse('triage')}?{urlencode(query_params)}" + ) + + context = self.get_context_data() + context["department"] = department + context["config"] = config + return self.render_to_response(context) + class GeometriciansView(MoulinetteMixin, FormView): template_name = "pages/geometricians.html" @@ -190,11 +236,11 @@ class AvailabilityInfo(TemplateView): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - context["configs_available"] = MoulinetteConfig.objects.filter( + context["configs_available"] = ConfigAmenagement.objects.filter( is_activated=True ).order_by("department") - context["configs_soon"] = MoulinetteConfig.objects.filter( + context["configs_soon"] = ConfigAmenagement.objects.filter( is_activated=False ).order_by("department") diff --git a/envergo/static/sass/project.scss b/envergo/static/sass/project.scss index 4dc8dde37..84c1e8ec5 100644 --- a/envergo/static/sass/project.scss +++ b/envergo/static/sass/project.scss @@ -1433,7 +1433,6 @@ main.home { } section#simulateur { - background-color: #e8edff; background-color: var(--info-950-100); h2 { @@ -1887,3 +1886,11 @@ main.demonstrateur_2150 { #moulinette-submit-button { min-width: 8em; } + +#department-search-title { + color: var(--blue-france-sun-113-625); +} + +#contacts_and_links { + background-color: var(--grey-975-100); +} diff --git a/envergo/static/sass/project_haie.scss b/envergo/static/sass/project_haie.scss index 0967d35fc..29f5f2251 100644 --- a/envergo/static/sass/project_haie.scss +++ b/envergo/static/sass/project_haie.scss @@ -8,3 +8,9 @@ div#moulinette { font-weight: bold !important; } } + +main.home { + section#simulateur { + background-color: var(--blue-france-850-200); + } +} diff --git a/envergo/templates/amenagement/moulinette/form.html b/envergo/templates/amenagement/moulinette/form.html index 332a3cc8c..dae011fec 100644 --- a/envergo/templates/amenagement/moulinette/form.html +++ b/envergo/templates/amenagement/moulinette/form.html @@ -9,7 +9,7 @@

Simulez votre projet en phase a method="post" novalidate autocomplete="off" - action="{% url "moulinette_result" %}" + action="{% url "moulinette_home" %}" id="moulinette-form"> {% csrf_token %} diff --git a/envergo/templates/amenagement/moulinette/result_available_soon.html b/envergo/templates/amenagement/moulinette/result_available_soon.html new file mode 100644 index 000000000..05dc3cdbd --- /dev/null +++ b/envergo/templates/amenagement/moulinette/result_available_soon.html @@ -0,0 +1,31 @@ +{% extends "moulinette/result_available_soon.html" %} + +{% block result %} + +

Simulation réglementaire du projet

+ +
+

Le simulateur Envergo est en cours de déploiement dans votre département.

+

Il sera disponible d'ici à quelques semaines au plus tard.

+
+ +

+ 👉 Testez le simulateur avec + cet exemple de projet : une extension de + 1250 m² d'un bâtiment près des rives de la Loire, soumise à + la Loi sur l'eau. +

+ +

💡 Inscrivez-vous pour être informé·e de l’ouverture d’EnvErgo à votre département.

+ + Signaler mon intérêt + +

+ ⏱ Vous pouvez également demander un avis réglementaire + sur un projet, réalisé par l'équipe EnvErgo. Réponse en trois jours ouvrés. +

+ +{% endblock %} diff --git a/envergo/templates/amenagement/moulinette/result_debug.html b/envergo/templates/amenagement/moulinette/result_debug.html index 67cd0e451..11a4c8d10 100644 --- a/envergo/templates/amenagement/moulinette/result_debug.html +++ b/envergo/templates/amenagement/moulinette/result_debug.html @@ -14,7 +14,7 @@

Données initiales

  • Surface créée: {{ created_surface }} m²
  • Surface finale: {{ final_surface }} m²
  • - Département : {{ moulinette.department }} + Département : {{ moulinette.department }}
  • diff --git a/envergo/templates/amenagement/moulinette/result_non_disponible.html b/envergo/templates/amenagement/moulinette/result_non_disponible.html new file mode 100644 index 000000000..2f7b9897b --- /dev/null +++ b/envergo/templates/amenagement/moulinette/result_non_disponible.html @@ -0,0 +1,23 @@ +{% extends "moulinette/result_non_disponible.html" %} + +{% load evaluations static pages %} + +{% block result %} + +

    Simulation réglementaire du projet

    + +
    +

    Le simulateur EnvErgo n'est pas encore déployé dans votre département.

    +

    + Il est pour l'instant disponible dans {% nb_available_depts %} départements. +

    +
    + +

    + 👉 Testez le simulateur avec + cet exemple de projet : une extension de 1250 m² d'un bâtiment près des rives de la Loire, soumise à la Loi sur l'eau. +

    + + {% include "_department_opt_in.html" %} + +{% endblock %} diff --git a/envergo/templates/haie/moulinette/_form_introduction.html b/envergo/templates/haie/moulinette/_form_introduction.html index 286b64638..ff522b648 100644 --- a/envergo/templates/haie/moulinette/_form_introduction.html +++ b/envergo/templates/haie/moulinette/_form_introduction.html @@ -4,4 +4,4 @@
    Il est anonyme et sans création de compte

    -

    Département : Indre (36)

    +

    Département : {{ department|default_if_none:"Inconnu" }}

    diff --git a/envergo/templates/haie/moulinette/form.html b/envergo/templates/haie/moulinette/form.html index ce916d908..489b67b1b 100644 --- a/envergo/templates/haie/moulinette/form.html +++ b/envergo/templates/haie/moulinette/form.html @@ -11,7 +11,7 @@

    Commencez à décrire votre projet de method="post" novalidate autocomplete="off" - action="{% url 'moulinette_result' %}" + action="{% url 'moulinette_home' %}?{{ request.GET.urlencode }}" id="moulinette-form"> {% csrf_token %} @@ -28,11 +28,6 @@

    Commencez à décrire votre projet de name="travaux" value="{{ request.GET.travaux|default:'' }}" /> - - - - -
    {% include '_radio_snippet.html' with field=form.profil %} {% include '_radio_snippet.html' with field=form.motif %} diff --git a/envergo/templates/haie/moulinette/result_non_disponible.html b/envergo/templates/haie/moulinette/result_non_disponible.html new file mode 100644 index 000000000..4497d1120 --- /dev/null +++ b/envergo/templates/haie/moulinette/result_non_disponible.html @@ -0,0 +1,14 @@ +{% extends "moulinette/result_non_disponible.html" %} + +{% load pages %} + +{% block result %} + +

    Simulation réglementaire du projet

    + +
    +

    Le guichet unique de la haie n'est pas encore déployé dans votre département.

    +

    Il est pour l'instant disponible dans {% nb_available_depts "haie" %} départements.

    +
    + +{% endblock %} diff --git a/envergo/templates/haie/moulinette/triage_result.html b/envergo/templates/haie/moulinette/triage_result.html index 9be262b65..d0346bc0c 100644 --- a/envergo/templates/haie/moulinette/triage_result.html +++ b/envergo/templates/haie/moulinette/triage_result.html @@ -4,7 +4,7 @@ {% block project_summary %} diff --git a/envergo/templates/haie/pages/home.html b/envergo/templates/haie/pages/home.html index f443fd18d..93d0120f5 100644 --- a/envergo/templates/haie/pages/home.html +++ b/envergo/templates/haie/pages/home.html @@ -10,7 +10,7 @@ {% block container %} -
    +
    {% comment %} @@ -63,6 +63,82 @@

    Le point d'accès unique à la réglementation et aux démarches administrat

    +
    +
    +

    Simuler un projet

    +

    + + Vous envisagez des travaux sur vos haies ? Nous vous informons sur les règles de protection et les procédures à + suivre. + +
    + 3 minutes, gratuit et anonyme +

    +
    + {% for department in activated_departments %} +
    + +
    + {% endfor %} +
    + +
    + Le projet est situé dans un autre département ? +
    +
    + {% csrf_token %} + +
    +
    +
    + {% if department and config and config.department_guichet_unique_url %} +

    Un guichet de la haie existe déjà pour le département {{ department }}.

    +

    + Aller au Guichet unique Haie pour ce + département +

    + {% elif department %} +
    +
    +
    +

    + + Le guichet unique haie n'existe pas encore dans le département {{ department }}. +

    +
    +
    +
    + {% if config and config.contacts_and_links %} + + {% endif %} + {% endif %} +
    +
    +
    @@ -97,10 +173,6 @@

    Quels travaux le guichet unique de la haie couvre-t-il actuell

    - -
    -
    {% show_moulinette_form %}
    -
    {% endblock %} {% block extra_js %} diff --git a/envergo/templates/moulinette/result_available_soon.html b/envergo/templates/moulinette/result_available_soon.html index 8a157ab78..c218607d7 100644 --- a/envergo/templates/moulinette/result_available_soon.html +++ b/envergo/templates/moulinette/result_available_soon.html @@ -1,37 +1,5 @@ {% extends base_result %} -{% load evaluations static %} - -{% block result %} - -

    Simulation réglementaire du projet

    - -
    -

    Le simulateur Envergo est en cours de déploiement dans votre département.

    -

    Il sera disponible d'ici à quelques semaines au plus tard.

    -
    - -

    - 👉 Testez le simulateur avec - cet exemple de projet : une extension de - 1250 m² d'un bâtiment près des rives de la Loire, soumise à - la Loi sur l'eau. -

    - -

    💡 Inscrivez-vous pour être informé·e de l’ouverture d’EnvErgo à votre département.

    - - Signaler mon intérêt - -

    - ⏱ Vous pouvez également demander un avis réglementaire - sur un projet, réalisé par l'équipe EnvErgo. Réponse en trois jours ouvrés. -

    - -{% endblock %} - {% block extra_js %} {{ block.super }}