diff --git a/.talismanrc b/.talismanrc index 9fd8faed40..dd804e7037 100644 --- a/.talismanrc +++ b/.talismanrc @@ -16,7 +16,7 @@ fileignoreconfig: - filename: erp/export/static/schema.json checksum: 888e09b1fe8ead28a4561b05842cd852be1ca8a3b86264aa48469514f93cfdd2 - filename: templates/contrib/6-accueil.html - checksum: 6a2b50478a202eb35bf49d50ff156110eeed68287d545626753b7343bd3e3074 + checksum: 8aaacce11ed6b610a2bd7556d5f42b5035747d994eb103ccdfe0fdec0ae1c708 - filename: templates/contrib/2-a-propos.html checksum: 674d89a3766a1df233648b7de14a5116de651bfea5b4db7f4f9f05f95629a32a - filename: templates/contrib/0-start.html @@ -28,7 +28,7 @@ fileignoreconfig: - filename: templates/contrib/4-exterieur.html checksum: 4e6911a34b51f640e0fb6a2dad359628daf63459f2b3d868ce0d80ce094f8538 - filename: templates/contrib/3-transport.html - checksum: 31cb1838db8607f44c92e00e0841c18f7454943ea3a600d79f943fae2f967874 + checksum: 68334d56087d71ffb7443ca8917ed32c553786a8b1c9852bec1adec03aa138fe - filename: .circleci/config.yml checksum: 9c50da347ce5a9602eb25d2c192597343c50e062f465c0e8ae6e4aed1034407e - filename: erp/imports/mapper/service_public.py @@ -52,7 +52,13 @@ fileignoreconfig: - filename: erp/management/commands/flush_widget_stats.py checksum: 07f7fb0fbd7c587d43aad69f87e892107a6e80c8d5a5145d608905485a4f2011 - filename: tests/stats/tests.py - checksum: 3d899b94bc836e0f97b282d569ca27b513be6df54cf718e96a944a99b634444d + checksum: 14316ca4cb3801fde82de4647e7c3df271a7c5b962e3526fa06e5d190caceb27 +- filename: tests/api/tests.py + checksum: 19cc87e6a7c33d056a2c85831e86af11ba1fff4935b9fc4b786faee0c88552c9 +- filename: tests/erp/test_tasks.py + checksum: 5af076ad122ca09cd7a76c75a11c3094c70d15fa928a827a3180c8c43177a9f5 +- filename: tests/erp/imports/test_serializers.py + checksum: ec01ecc27f54e45f02eae36db0369b353e069426f19eeac2025d776760f94fec - filename: .env.sample checksum: aa3f02c8f5d30f989986f9eafaca8bc57d99d09361b9fa9c2158638bcc263f15 - filename: erp/management/commands/import_apidae.py @@ -93,3 +99,9 @@ fileignoreconfig: checksum: 40161683c6d36b3a1d5bb3c0b05594fa48ada9dd88ecc379294fa10f64743eb2 - filename: core/settings.py checksum: 541b4298f5183e65805309ef55140f6b1da02f62a1fbf196fba3cc8d7c5dab58 +- filename: erp/models.py + checksum: 7399cf142d418045c6167e0ed5b83c3f61a9102015d994929e0584f288508e67 +- filename: erp/schema.py + checksum: 319eee5047b7058a601da24eb201f2ed8fd4ece23e9dc47a92f05f2e956e7ae2 +- filename: erp/migrations/0191_remove_accessibilite_erp_accessibilite_sanitaires_presence_consistency_and_more.py + checksum: 9549fe2888e7d35c031c145eca2235510545a0b69774201c1365f466ca42f649 \ No newline at end of file diff --git a/erp/forms.py b/erp/forms.py index 2f0f8a1967..1a92224b51 100644 --- a/erp/forms.py +++ b/erp/forms.py @@ -316,6 +316,115 @@ def clean_accueil_soignant_experience(self): return self.cleaned_data["accueil_soignant_experience"] +class ContribAccessibiliteSportsEquipmentForm(ContribAccessibiliteForm): + accueil_casiers_fermeture = forms.MultipleChoiceField( + required=False, + choices=schema.ACCUEIL_SYSTEME_FERMETURE_CHOICES, + widget=forms.CheckboxSelectMultiple, + label=schema.get_label("accueil_casiers_fermeture"), + help_text=schema.get_help_text("accueil_casiers_fermeture"), + ) + + accueil_prestations_complementaires = forms.MultipleChoiceField( + required=False, + choices=schema.ACCUEIL_PRESTATIONS_COMPLEMENTAIRES_CHOICES, + widget=forms.CheckboxSelectMultiple, + label=schema.get_label("accueil_prestations_complementaires"), + help_text=schema.get_help_text("accueil_prestations_complementaires"), + ) + + accueil_presence_espaces_specifiques = forms.MultipleChoiceField( + required=False, + choices=schema.ACCUEIL_PRESENCE_ESPACES_SPECIFIQUES_CHOICES, + widget=forms.CheckboxSelectMultiple, + label=schema.get_label("accueil_presence_espaces_specifiques"), + help_text=schema.get_help_text("accueil_presence_espaces_specifiques"), + ) + + fields_to_remove = ( + "accueil_personnels", # Root + "accueil_audiodescription_presence", # Root + "accueil_audiodescription", # Child + "accueil_ascenseur_etage", # Root + "accueil_ascenseur_etage_pmr", # Child + "labels", # Root + "labels_familles_handicap", # Root + "labels_autre", # Root + ) + + large_establishments_fields_to_keep = ["accueil_signaletique_interieure", "cheminement_ext_signaletique_exterieure"] + conditionals_to_add = get_conditional_fields_in(["sports_equipment"]) + large_establishments_fields_to_keep + conditionals_to_remove = get_conditional_fields_not_in(["sports_equipment"]) + + class Meta: + model = Accessibilite + exclude = ("pk",) + widgets = get_widgets_for_accessibilite() + labels = schema.get_labels(include_conditional=["sports_equipment", "large_establishments"]) + help_texts = schema.get_help_texts(include_conditional=["sports_equipment", "large_establishments"]) + required = schema.get_required_fields() + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + allowed_fields = set(schema.get_help_texts(include_conditional=["sports_equipment"]).keys()) | set( + self.large_establishments_fields_to_keep + ) + for field in copy(self.fields): + if field not in allowed_fields: + self.fields.pop(field, None) + + def clean_accueil_casiers_adaptes(self): + if self.cleaned_data.get("accueil_casiers") is not True: + return None + return self.cleaned_data["accueil_casiers_adaptes"] + + def clean_accueil_casiers_fermeture(self): + if self.cleaned_data.get("accueil_casiers") is not True: + return None + return self.cleaned_data["accueil_casiers_fermeture"] + + def clean_accueil_tribunes_localisation_places(self): + if (self.cleaned_data.get("accueil_tribunes_places") or 0) < 1: + return None + return self.cleaned_data["accueil_tribunes_localisation_places"] + + def clean_accueil_tribunes_places_avec_accompagnants(self): + if (self.cleaned_data.get("accueil_tribunes_places") or 0) < 1: + return None + return self.cleaned_data["accueil_tribunes_places_avec_accompagnants"] + + def clean_accueil_vestiaires_largeur_passage(self): + if self.cleaned_data.get("accueil_vestiaires") is not True: + return None + return self.cleaned_data["accueil_vestiaires_largeur_passage"] + + def clean_accueil_douches_collectives_adaptees(self): + if self.cleaned_data.get("accueil_douches_collectives") is not True: + return None + return self.cleaned_data["accueil_douches_collectives_adaptees"] + + def clean_accueil_douches_individuelles_adaptees(self): + if self.cleaned_data.get("accueil_douches_individuelles") is not True: + return None + return self.cleaned_data["accueil_douches_individuelles_adaptees"] + + def clean_sanitaires_urinoirs(self): + if self.cleaned_data.get("sanitaires_presence") is not True: + return None + return self.cleaned_data["sanitaires_urinoirs"] + + def clean_sanitaires_largeur_porte(self): + if self.cleaned_data.get("sanitaires_adaptes") is not True: + return None + return self.cleaned_data["sanitaires_largeur_porte"] + + def clean_sanitaires_sens_transfert(self): + if self.cleaned_data.get("sanitaires_adaptes") is not True: + return None + return self.cleaned_data["sanitaires_sens_transfert"] + + class AdminAccessibiliteForm(ContribAccessibiliteForm): # Note: defining `labels` and `help_texts` in `Meta` doesn't work with custom # fields, hence why we set them up manually for each fields. @@ -942,6 +1051,7 @@ def get_contrib_forms_for_activity(activity: Activite): ACTIVITY_GROUPS["LARGE_ESTABLISHMENTS"]: ContribAccessibiliteLargeEstablishmentsForm, ACTIVITY_GROUPS["POLLING_STATION"]: ContribAccessibilitePollingStationForm, ACTIVITY_GROUPS["HEALTHCARE"]: ContribAccessibiliteHealthcareForm, + ACTIVITY_GROUPS["SPORTS_EQUIPMENT"]: ContribAccessibiliteSportsEquipmentForm, } groups = activity.groups.all() diff --git a/erp/imports/serializers.py b/erp/imports/serializers.py index 5cc43fadea..0027e98a7f 100644 --- a/erp/imports/serializers.py +++ b/erp/imports/serializers.py @@ -53,6 +53,7 @@ class AccessibilityImportSerializer(serializers.ModelSerializer): stationnement_pmr = NullBooleanField(required=False, allow_null=True) stationnement_ext_presence = NullBooleanField(required=False, allow_null=True) stationnement_ext_pmr = NullBooleanField(required=False, allow_null=True) + stationnement_zone_depose_pmr = NullBooleanField(required=False, allow_null=True) cheminement_ext_presence = NullBooleanField(required=False, allow_null=True) cheminement_ext_terrain_stable = NullBooleanField(required=False, allow_null=True) cheminement_ext_plain_pied = NullBooleanField(required=False, allow_null=True) @@ -95,8 +96,18 @@ class AccessibilityImportSerializer(serializers.ModelSerializer): accueil_soignant = NullBooleanField(required=False, allow_null=True) accueil_salle_consultation_accessible = NullBooleanField(required=False, allow_null=True) accueil_consultation_domicile = NullBooleanField(required=False, allow_null=True) + accueil_aire_de_jeux = NullBooleanField(required=False, allow_null=True) + accueil_tribunes = NullBooleanField(required=False, allow_null=True) + accueil_vestiaires = NullBooleanField(required=False, allow_null=True) + accueil_douches_collectives = NullBooleanField(required=False, allow_null=True) + accueil_douches_collectives_adaptees = NullBooleanField(required=False, allow_null=True) + accueil_douches_individuelles = NullBooleanField(required=False, allow_null=True) + accueil_douches_individuelles_adaptees = NullBooleanField(required=False, allow_null=True) + accueil_casiers = NullBooleanField(required=False, allow_null=True) + accueil_casiers_adaptes = NullBooleanField(required=False, allow_null=True) sanitaires_presence = NullBooleanField(required=False, allow_null=True) sanitaires_adaptes = NullBooleanField(required=False, allow_null=True) + sanitaires_urinoirs = NullBooleanField(required=False, allow_null=True) conformite = NullBooleanField(required=False, allow_null=True) class Meta: @@ -222,15 +233,22 @@ def validate_accessibilite(self, obj): return obj + @staticmethod + def _should_display_children(field_config, current_value): + min_value = field_config.get("min_value_to_display_children") + if min_value is not None: + return current_value is not None and current_value >= min_value + + return str(current_value) in field_config.get("value_to_display_children", []) + def _handle_children_reinit(self, accessibilite_instance, field_name): field_config = FIELDS.get(field_name) if not field_config or not field_config.get("children"): return current_value = getattr(accessibilite_instance, field_name) - trigger_values = field_config.get("value_to_display_children", []) - if str(current_value) not in trigger_values: + if not self._should_display_children(field_config, current_value): for child_name in field_config.get("children"): field_obj = accessibilite_instance._meta.get_field(child_name) default_value = field_obj.get_default() diff --git a/erp/migrations/0191_remove_accessibilite_erp_accessibilite_sanitaires_presence_consistency_and_more.py b/erp/migrations/0191_remove_accessibilite_erp_accessibilite_sanitaires_presence_consistency_and_more.py new file mode 100644 index 0000000000..a77c0147c2 --- /dev/null +++ b/erp/migrations/0191_remove_accessibilite_erp_accessibilite_sanitaires_presence_consistency_and_more.py @@ -0,0 +1,428 @@ +# Generated by Django 6.0.7 on 2026-08-03 20:32 + +import django.contrib.postgres.fields +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("erp", "0190_rpa"), + ] + + operations = [ + migrations.RemoveConstraint( + model_name="accessibilite", + name="erp_accessibilite_sanitaires_presence_consistency", + ), + migrations.AddField( + model_name="accessibilite", + name="accueil_aire_de_jeux", + field=models.BooleanField( + blank=True, + choices=[(True, "Oui"), (False, "Non"), (None, "Inconnu")], + null=True, + verbose_name="Accessibilité de l'aire de jeux à une personne en fauteuil", + ), + ), + migrations.AddField( + model_name="accessibilite", + name="accueil_casiers", + field=models.BooleanField( + blank=True, + choices=[(True, "Oui"), (False, "Non"), (None, "Inconnu")], + null=True, + verbose_name="Présence de casiers", + ), + ), + migrations.AddField( + model_name="accessibilite", + name="accueil_casiers_adaptes", + field=models.BooleanField( + blank=True, + choices=[(True, "Oui"), (False, "Non"), (None, "Inconnu")], + null=True, + verbose_name="Casiers adaptés", + ), + ), + migrations.AddField( + model_name="accessibilite", + name="accueil_casiers_fermeture", + field=django.contrib.postgres.fields.ArrayField( + base_field=models.CharField( + blank=True, + choices=[ + ("serrure_avec_cle", "Serrure avec clé"), + ("serrure_electronique_a_code", "Serrure électronique à code"), + ("serrure_mecanique_a_code", "Serrure mécanique à code"), + ("verrou_cadenas", "Verrou pour cadenas personnel"), + ("autre", "Autre"), + ], + max_length=255, + ), + blank=True, + default=list, + null=True, + verbose_name="Système de fermeture des casiers", + ), + ), + migrations.AddField( + model_name="accessibilite", + name="accueil_douches_collectives", + field=models.BooleanField( + blank=True, + choices=[(True, "Oui"), (False, "Non"), (None, "Inconnu")], + null=True, + verbose_name="Présence de douches collectives", + ), + ), + migrations.AddField( + model_name="accessibilite", + name="accueil_douches_collectives_adaptees", + field=models.BooleanField( + blank=True, + choices=[(True, "Oui"), (False, "Non"), (None, "Inconnu")], + null=True, + verbose_name="Accessibilité des douches collectives", + ), + ), + migrations.AddField( + model_name="accessibilite", + name="accueil_douches_individuelles", + field=models.BooleanField( + blank=True, + choices=[(True, "Oui"), (False, "Non"), (None, "Inconnu")], + null=True, + verbose_name="Présence de douches individuelles", + ), + ), + migrations.AddField( + model_name="accessibilite", + name="accueil_douches_individuelles_adaptees", + field=models.BooleanField( + blank=True, + choices=[(True, "Oui"), (False, "Non"), (None, "Inconnu")], + null=True, + verbose_name="Accessibilité des douches individuelles", + ), + ), + migrations.AddField( + model_name="accessibilite", + name="accueil_physique", + field=models.CharField( + blank=True, + choices=[ + ("inexistant", "Aucun personnel"), + ("non_forme", "Personnel non formé"), + ("sensibilise_ou_forme", "Personne sensibilisé ou formé"), + (None, "Inconnu"), + ], + max_length=25, + null=True, + verbose_name="Accueil physique", + ), + ), + migrations.AddField( + model_name="accessibilite", + name="accueil_presence_espaces_specifiques", + field=django.contrib.postgres.fields.ArrayField( + base_field=models.CharField( + blank=True, + choices=[ + ( + "presence_espace_depose_equipements", + "Espace de dépose d'équipements (fauteuils, déambulateurs...)", + ), + ( + "presence_espace_repos_sensoriel", + "Espace de repos sensoriel (hyposensoriel, d'apaisement...)", + ), + ("presence_espace_chiens_guides", "Espace destiné aux chiens guides et d'assistance"), + ( + "presence_espace_salle_de_change", + "Salle de change destinée aux personnes ayant des handicaps multiples ou complexes", + ), + ], + max_length=255, + ), + blank=True, + default=list, + null=True, + verbose_name="Présence d’espaces spécifiques", + ), + ), + migrations.AddField( + model_name="accessibilite", + name="accueil_prestations_complementaires", + field=django.contrib.postgres.fields.ArrayField( + base_field=models.CharField( + blank=True, + choices=[ + ("sonorisation_arbitrage", "Sonorisation de l'arbitrage possible"), + ("score_visible", "Score visible depuis les places assises"), + ( + "prestation_espace_depose_equipements", + "Espace de dépose d'équipements (fauteuils, déambulateurs...)", + ), + ("reservation_place_facile_d_acces", "Réservation possible de places facile d'accès"), + ], + max_length=255, + ), + blank=True, + default=list, + null=True, + verbose_name="Prestations complémentaires", + ), + ), + migrations.AddField( + model_name="accessibilite", + name="accueil_tribunes", + field=models.BooleanField( + blank=True, + choices=[(True, "Oui"), (False, "Non"), (None, "Inconnu")], + null=True, + verbose_name="Accessibilité des places des tribunes", + ), + ), + migrations.AddField( + model_name="accessibilite", + name="accueil_tribunes_localisation_places", + field=models.CharField( + blank=True, + choices=[ + ("niveau_aire_de_jeux", "Places situées au niveau de l'aire de jeux"), + ("reparti_differents_niveaux", "Places répartis différents niveaux des tribunes"), + (None, "Inconnu"), + ], + max_length=100, + null=True, + verbose_name="Localisation des places dans les tribunes", + ), + ), + migrations.AddField( + model_name="accessibilite", + name="accueil_tribunes_places", + field=models.PositiveSmallIntegerField( + blank=True, + null=True, + verbose_name="Nombre de places accessibles pour les personnes en fauteuil roulant", + ), + ), + migrations.AddField( + model_name="accessibilite", + name="accueil_tribunes_places_avec_accompagnants", + field=models.PositiveSmallIntegerField( + blank=True, null=True, verbose_name="Nombre de places pour les accompagnants dans les tribunes" + ), + ), + migrations.AddField( + model_name="accessibilite", + name="accueil_vestiaires", + field=models.BooleanField( + blank=True, + choices=[(True, "Oui"), (False, "Non"), (None, "Inconnu")], + null=True, + verbose_name="Accessibilité des vestiaires", + ), + ), + migrations.AddField( + model_name="accessibilite", + name="accueil_vestiaires_largeur_passage", + field=models.CharField( + blank=True, + choices=[ + ("entre_90_et_110", "Entre 90 et 110 cm"), + ("superieur_a_110", "Supérieure à 110 cm"), + (None, "Inconnu"), + ], + max_length=40, + null=True, + verbose_name="Largeur de passage pour se rendre dans les vestiaires", + ), + ), + migrations.AddField( + model_name="accessibilite", + name="sanitaires_largeur_porte", + field=models.CharField( + blank=True, + choices=[ + ("entre_80_et_90", "Entre 80 et 90 cm"), + ("entre_90_et_110", "Entre 90 et 110 cm"), + (None, "Inconnu"), + ], + max_length=40, + null=True, + verbose_name="Largeur de porte des toilettes", + ), + ), + migrations.AddField( + model_name="accessibilite", + name="sanitaires_sens_transfert", + field=models.CharField( + blank=True, + choices=[ + ("gauche", "Gauche"), + ("droite", "Droite"), + ("gauche_et_droite", "Gauche et droite"), + (None, "Inconnu"), + ], + max_length=40, + null=True, + verbose_name="Sens du transfert", + ), + ), + migrations.AddField( + model_name="accessibilite", + name="sanitaires_urinoirs", + field=models.BooleanField( + blank=True, + choices=[(True, "Oui"), (False, "Non"), (None, "Inconnu")], + null=True, + verbose_name="Hauteur des urinoirs", + ), + ), + migrations.AddField( + model_name="accessibilite", + name="stationnement_zone_depose_pmr", + field=models.BooleanField( + blank=True, + choices=[(True, "Oui"), (False, "Non"), (None, "Inconnu")], + null=True, + verbose_name="Aire de dépose PMR", + ), + ), + migrations.AlterField( + model_name="accessibilite", + name="accueil_prise_en_charge_patients", + field=django.contrib.postgres.fields.ArrayField( + base_field=models.CharField( + blank=True, + choices=[ + ("preparation", "Préparation à la consultation ou visite préalable"), + ("organisation", "Organisation spécifique pour réduire le temps d’attente"), + ("outils_communication", "Outils de communication simplifiés pour expliquer les actes"), + ("materiel_auscultation", "Matériel médical adapté aux personnes obèses"), + ], + max_length=255, + ), + blank=True, + default=list, + null=True, + verbose_name="Prise en charge des patients", + ), + ), + migrations.AlterField( + model_name="accessibilite", + name="accueil_soignant_experience", + field=django.contrib.postgres.fields.ArrayField( + base_field=models.CharField( + blank=True, + choices=[ + ("visuel", "Visuel"), + ("auditif", "Auditif"), + ("moteur", "Moteur"), + ("mental", "Mental (déficience intellectuelle)"), + ("psychique", "Psychique (psychose, trouble bipolaire)"), + ("cognitif", "Cognitif (trouble du spectre de l'autisme, TDAH...)"), + ], + max_length=255, + ), + blank=True, + default=list, + null=True, + verbose_name="Expérience du soignant concernant les types de handicap", + ), + ), + migrations.AlterField( + model_name="erp", + name="checked_up_to_date_at", + field=models.DateTimeField( + auto_now_add=True, null=True, verbose_name="Dernière vérification des informations" + ), + ), + migrations.AddConstraint( + model_name="accessibilite", + constraint=models.CheckConstraint( + condition=models.Q( + models.Q(("sanitaires_presence", True), ("sanitaires_presence__isnull", False)), + models.Q(("sanitaires_adaptes", None), ("sanitaires_urinoirs", None)), + _connector="OR", + ), + name="erp_accessibilite_sanitaires_presence_consistency", + ), + ), + migrations.AddConstraint( + model_name="accessibilite", + constraint=models.CheckConstraint( + condition=models.Q( + models.Q(("sanitaires_adaptes", True), ("sanitaires_adaptes__isnull", False)), + models.Q( + models.Q( + ("sanitaires_largeur_porte__isnull", True), + ("sanitaires_largeur_porte", ""), + _connector="OR", + ), + models.Q( + ("sanitaires_sens_transfert__isnull", True), + ("sanitaires_sens_transfert", ""), + _connector="OR", + ), + ), + _connector="OR", + ), + name="erp_accessibilite_sanitaires_adaptes_consistency", + ), + ), + migrations.AddConstraint( + model_name="accessibilite", + constraint=models.CheckConstraint( + condition=models.Q( + models.Q(("accueil_casiers", True), ("accueil_casiers__isnull", False)), + models.Q( + ("accueil_casiers_adaptes", None), + models.Q( + ("accueil_casiers_fermeture__isnull", True), + ("accueil_casiers_fermeture", []), + _connector="OR", + ), + ), + _connector="OR", + ), + name="erp_accessibilite_accueil_casiers_consistency", + ), + ), + migrations.AddConstraint( + model_name="accessibilite", + constraint=models.CheckConstraint( + condition=models.Q( + models.Q( + ("accueil_tribunes_places", 0), + ("accueil_tribunes_places__isnull", True), + _connector="OR", + _negated=True, + ), + models.Q( + models.Q( + ("accueil_tribunes_localisation_places__isnull", True), + ("accueil_tribunes_localisation_places", ""), + _connector="OR", + ), + ("accueil_tribunes_places_avec_accompagnants__isnull", True), + ), + _connector="OR", + ), + name="erp_accessibilite_accueil_tribunes_places_consistency", + ), + ), + migrations.AddConstraint( + model_name="accessibilite", + constraint=models.CheckConstraint( + condition=models.Q( + models.Q(("accueil_vestiaires", True), ("accueil_vestiaires__isnull", False)), + ("accueil_vestiaires_largeur_passage__isnull", True), + ("accueil_vestiaires_largeur_passage", ""), + _connector="OR", + ), + name="erp_accessibilite_accueil_vestiaires_consistency", + ), + ), + ] diff --git a/erp/models.py b/erp/models.py index 901cfcc16b..3151323599 100644 --- a/erp/models.py +++ b/erp/models.py @@ -44,6 +44,7 @@ "LARGE_ESTABLISHMENTS": "Grands établissements", "POLLING_STATION": "Bureau de vote", "HEALTHCARE": "Santé", + "SPORTS_EQUIPMENT": "Équipements sportifs", } @@ -1265,7 +1266,15 @@ class Meta: CheckConstraint( name="%(app_label)s_%(class)s_sanitaires_presence_consistency", condition=(Q(sanitaires_presence=True) & Q(sanitaires_presence__isnull=False)) - | Q(sanitaires_adaptes=None), + | (Q(sanitaires_adaptes=None) & Q(sanitaires_urinoirs=None)), + ), + CheckConstraint( + name="%(app_label)s_%(class)s_sanitaires_adaptes_consistency", + condition=(Q(sanitaires_adaptes=True) & Q(sanitaires_adaptes__isnull=False)) + | ( + (Q(sanitaires_largeur_porte__isnull=True) | Q(sanitaires_largeur_porte="")) + & (Q(sanitaires_sens_transfert__isnull=True) | Q(sanitaires_sens_transfert="")) + ), ), CheckConstraint( name="%(app_label)s_%(class)s_labels_consistency", @@ -1297,6 +1306,36 @@ class Meta: | (Q(accueil_soignant_experience__isnull=True) | Q(accueil_soignant_experience=[])) ), ), + CheckConstraint( + name="%(app_label)s_%(class)s_accueil_casiers_consistency", + condition=( + (Q(accueil_casiers=True) & Q(accueil_casiers__isnull=False)) + | ( + Q(accueil_casiers_adaptes=None) + & (Q(accueil_casiers_fermeture__isnull=True) | Q(accueil_casiers_fermeture=[])) + ) + ), + ), + CheckConstraint( + name="%(app_label)s_%(class)s_accueil_tribunes_places_consistency", + condition=( + ~(Q(accueil_tribunes_places=0) | Q(accueil_tribunes_places__isnull=True)) + | ( + ( + Q(accueil_tribunes_localisation_places__isnull=True) + | Q(accueil_tribunes_localisation_places="") + ) + & Q(accueil_tribunes_places_avec_accompagnants__isnull=True) + ) + ), + ), + CheckConstraint( + name="%(app_label)s_%(class)s_accueil_vestiaires_consistency", + condition=( + (Q(accueil_vestiaires=True) & Q(accueil_vestiaires__isnull=False)) + | (Q(accueil_vestiaires_largeur_passage__isnull=True) | Q(accueil_vestiaires_largeur_passage="")) + ), + ), ] erp = models.OneToOneField( @@ -1361,6 +1400,12 @@ class Meta: choices=schema.get_field_choices("stationnement_ext_pmr"), verbose_name=translate_lazy("Stationnements PMR à proximité de l'ERP"), ) + stationnement_zone_depose_pmr = models.BooleanField( + null=True, + blank=True, + choices=schema.get_field_choices("stationnement_zone_depose_pmr"), + verbose_name=translate_lazy("Aire de dépose PMR"), + ) ################################### # Espace et Cheminement extérieur # @@ -1897,6 +1942,112 @@ class Meta: blank=True, verbose_name=translate_lazy("Prise en charge des patients"), ) + accueil_physique = models.CharField( + max_length=25, + null=True, + blank=True, + verbose_name=translate_lazy("Accueil physique"), + choices=schema.ACCUEIL_PHYSIQUE_CHOICES, + ) + accueil_aire_de_jeux = models.BooleanField( + null=True, + blank=True, + choices=schema.get_field_choices("accueil_aire_de_jeux"), + verbose_name=translate_lazy("Accessibilité de l'aire de jeux à une personne en fauteuil"), + ) + accueil_tribunes = models.BooleanField( + null=True, + blank=True, + choices=schema.get_field_choices("accueil_tribunes"), + verbose_name=translate_lazy("Accessibilité des places des tribunes"), + ) + accueil_tribunes_places = models.PositiveSmallIntegerField( + null=True, + blank=True, + verbose_name=translate_lazy("Nombre de places accessibles pour les personnes en fauteuil roulant"), + ) + accueil_tribunes_localisation_places = models.CharField( + max_length=100, + null=True, + blank=True, + verbose_name=translate_lazy("Localisation des places dans les tribunes"), + choices=schema.ACCUEIL_TRIBUNES_PLACES_CHOICES, + ) + accueil_tribunes_places_avec_accompagnants = models.PositiveSmallIntegerField( + null=True, + blank=True, + verbose_name=translate_lazy("Nombre de places pour les accompagnants dans les tribunes"), + ) + accueil_vestiaires = models.BooleanField( + null=True, + blank=True, + choices=schema.get_field_choices("accueil_vestiaires"), + verbose_name=translate_lazy("Accessibilité des vestiaires"), + ) + accueil_vestiaires_largeur_passage = models.CharField( + max_length=40, + null=True, + blank=True, + verbose_name=translate_lazy("Largeur de passage pour se rendre dans les vestiaires"), + choices=schema.ACCUEIL_VESTIAIRES_LARGEUR_PASSAGE_CHOICES, + ) + accueil_douches_collectives = models.BooleanField( + null=True, + blank=True, + choices=schema.get_field_choices("accueil_douches_collectives"), + verbose_name=translate_lazy("Présence de douches collectives"), + ) + accueil_douches_collectives_adaptees = models.BooleanField( + null=True, + blank=True, + choices=schema.get_field_choices("accueil_douches_collectives_adaptees"), + verbose_name=translate_lazy("Accessibilité des douches collectives"), + ) + accueil_douches_individuelles = models.BooleanField( + null=True, + blank=True, + choices=schema.get_field_choices("accueil_douches_individuelles"), + verbose_name=translate_lazy("Présence de douches individuelles"), + ) + accueil_douches_individuelles_adaptees = models.BooleanField( + null=True, + blank=True, + choices=schema.get_field_choices("accueil_douches_individuelles_adaptees"), + verbose_name=translate_lazy("Accessibilité des douches individuelles"), + ) + accueil_casiers = models.BooleanField( + null=True, + blank=True, + choices=schema.get_field_choices("accueil_casiers"), + verbose_name=translate_lazy("Présence de casiers"), + ) + accueil_casiers_adaptes = models.BooleanField( + null=True, + blank=True, + choices=schema.get_field_choices("accueil_casiers_adaptes"), + verbose_name=translate_lazy("Casiers adaptés"), + ) + accueil_casiers_fermeture = ArrayField( + models.CharField(max_length=255, blank=True, choices=schema.ACCUEIL_SYSTEME_FERMETURE_CHOICES), + default=list, + null=True, + blank=True, + verbose_name=translate_lazy("Système de fermeture des casiers"), + ) + accueil_prestations_complementaires = ArrayField( + models.CharField(max_length=255, blank=True, choices=schema.ACCUEIL_PRESTATIONS_COMPLEMENTAIRES_CHOICES), + verbose_name=translate_lazy("Prestations complémentaires"), + default=list, + null=True, + blank=True, + ) + accueil_presence_espaces_specifiques = ArrayField( + models.CharField(max_length=255, blank=True, choices=schema.ACCUEIL_PRESENCE_ESPACES_SPECIFIQUES_CHOICES), + verbose_name=translate_lazy("Présence d’espaces spécifiques"), + default=list, + null=True, + blank=True, + ) ############## # Sanitaires # ############## @@ -1906,14 +2057,32 @@ class Meta: choices=schema.get_field_choices("sanitaires_presence"), verbose_name=translate_lazy("Sanitaires"), ) - sanitaires_adaptes = models.BooleanField( null=True, blank=True, choices=schema.get_field_choices("sanitaires_adaptes"), verbose_name=translate_lazy("Nombre de sanitaires adaptés"), ) - + sanitaires_largeur_porte = models.CharField( + max_length=40, + null=True, + blank=True, + verbose_name=translate_lazy("Largeur de porte des toilettes"), + choices=schema.SANITAIRES_LARGEUR_PORTE_CHOICES, + ) + sanitaires_sens_transfert = models.CharField( + max_length=40, + null=True, + blank=True, + verbose_name=translate_lazy("Sens du transfert"), + choices=schema.SANITAIRES_SENS_TRANSFERT_CHOICES, + ) + sanitaires_urinoirs = models.BooleanField( + null=True, + blank=True, + verbose_name=translate_lazy("Hauteur des urinoirs"), + choices=schema.get_field_choices("sanitaires_urinoirs"), + ) ########## # labels # ########## @@ -2138,6 +2307,51 @@ def get_accueil_soignant_experience(self): for soignant_experience in self.accueil_soignant_experience ] + def get_accueil_tribunes_places_avec_accompagnants(self): + field = schema.FIELDS["accueil_tribunes_places_avec_accompagnants"] + if not self.accueil_tribunes_places_avec_accompagnants: + return str(field["help_text_ui_neg"]) + + return "{} : {}".format(field["help_text_ui"], self.accueil_tribunes_places_avec_accompagnants) + + def get_sanitaires_largeur_porte(self): + text = schema.FIELDS["sanitaires_largeur_porte"]["help_text_ui"] + " " + self.sanitaires_largeur_porte + return text + + def get_accueil_tribunes_places(self): + return "{} {}".format(self.accueil_tribunes_places, schema.FIELDS["accueil_tribunes_places"]["help_text_ui"]) + + def get_accueil_casiers_fermeture(self): + if not self.accueil_casiers_fermeture: + return + accueil_casiers_fermeture_text = {k: str(v) for k, v in schema.ACCUEIL_SYSTEME_FERMETURE_CHOICES} + return [ + accueil_casiers_fermeture_text.get(casiers_fermeture) + for casiers_fermeture in self.accueil_casiers_fermeture + ] + + def get_accueil_prestations_complementaires(self): + if not self.accueil_prestations_complementaires: + return + accueil_prestations_complementaires_text = { + k: str(v) for k, v in schema.ACCUEIL_PRESTATIONS_COMPLEMENTAIRES_CHOICES + } + return [ + accueil_prestations_complementaires_text.get(prestations_complementaires) + for prestations_complementaires in self.accueil_prestations_complementaires + ] + + def get_accueil_presence_espaces_specifiques(self): + if not self.accueil_presence_espaces_specifiques: + return + accueil_presence_espaces_specifiques_text = { + k: str(v) for k, v in schema.ACCUEIL_PRESENCE_ESPACES_SPECIFIQUES_CHOICES + } + return [ + accueil_presence_espaces_specifiques_text.get(presence_espaces_specifiques) + for presence_espaces_specifiques in self.accueil_presence_espaces_specifiques + ] + def get_accueil_prise_en_charge_patients(self): if not self.accueil_prise_en_charge_patients: return diff --git a/erp/schema.py b/erp/schema.py index 29de896f69..2a9693762a 100644 --- a/erp/schema.py +++ b/erp/schema.py @@ -151,6 +151,106 @@ ), ] +ACCUEIL_PHYSIQUE_INEXISTANT = "inexistant" +ACCUEIL_PHYSIQUE_NON_FORME = "non_forme" +ACCUEIL_PHYSIQUE_SENSIBILISE_OU_FORME = "sensibilise_ou_forme" +ACCUEIL_PHYSIQUE_CHOICES = [ + (ACCUEIL_PHYSIQUE_INEXISTANT, translate_lazy("Aucun personnel")), + (ACCUEIL_PHYSIQUE_NON_FORME, translate_lazy("Personnel non formé")), + (ACCUEIL_PHYSIQUE_SENSIBILISE_OU_FORME, translate_lazy("Personne sensibilisé ou formé")), + (None, UNKNOWN), +] + +ACCUEIL_TRIBUNES_PLACES_AIRE_DE_JEUX = "niveau_aire_de_jeux" +ACCUEIL_TRIBUNES_PLACES_REPARTIES_DIFFERENTS_NIVEAUX = "reparti_differents_niveaux" +ACCUEIL_TRIBUNES_PLACES_CHOICES = [ + (ACCUEIL_TRIBUNES_PLACES_AIRE_DE_JEUX, translate_lazy("Places situées au niveau de l'aire de jeux")), + ( + ACCUEIL_TRIBUNES_PLACES_REPARTIES_DIFFERENTS_NIVEAUX, + translate_lazy("Places répartis différents niveaux des tribunes"), + ), + (None, UNKNOWN), +] + +ACCUEIL_VESTIAIRES_LARGEUR_PASSAGE_ENTRE_90_ET_110 = "entre_90_et_110" +ACCUEIL_VESTIAIRES_LARGEUR_PASSAGE_SUPERIEUR_A_110 = "superieur_a_110" +ACCUEIL_VESTIAIRES_LARGEUR_PASSAGE_CHOICES = [ + (ACCUEIL_VESTIAIRES_LARGEUR_PASSAGE_ENTRE_90_ET_110, translate_lazy("Entre 90 et 110 cm")), + (ACCUEIL_VESTIAIRES_LARGEUR_PASSAGE_SUPERIEUR_A_110, translate_lazy("Supérieure à 110 cm")), + (None, UNKNOWN), +] + +SANITAIRES_LARGEUR_PORTE_ENTRE_80_et_90 = "entre_80_et_90" +SANITAIRES_LARGEUR_PORTE_ENTRE_90_ET_110 = "entre_90_et_110" +SANITAIRES_LARGEUR_PORTE_CHOICES = [ + (SANITAIRES_LARGEUR_PORTE_ENTRE_80_et_90, translate_lazy("Entre 80 et 90 cm")), + (SANITAIRES_LARGEUR_PORTE_ENTRE_90_ET_110, translate_lazy("Entre 90 et 110 cm")), + (None, UNKNOWN), +] + +SANITAIRES_SENS_TRANSFERT_GAUCHE = "gauche" +SANITAIRES_SENS_TRANSFERT_DROITE = "droite" +SANITAIRES_SENS_TRANSFERT_GAUCHE_ET_DROITE = "gauche_et_droite" +SANITAIRES_SENS_TRANSFERT_CHOICES = [ + (SANITAIRES_SENS_TRANSFERT_GAUCHE, translate_lazy("Gauche")), + (SANITAIRES_SENS_TRANSFERT_DROITE, translate_lazy("Droite")), + (SANITAIRES_SENS_TRANSFERT_GAUCHE_ET_DROITE, translate_lazy("Gauche et droite")), + (None, UNKNOWN), +] + +ACCUEIL_SYSTEME_FERMETURE_SERRURE_AVEC_CLE = "serrure_avec_cle" +ACCUEIL_SYSTEME_FERMETURE_ELECTRONIQUE_A_CODE = "serrure_electronique_a_code" +ACCUEIL_SYSTEME_FERMETURE_MECANIQUE_A_CODE = "serrure_mecanique_a_code" +ACCUEIL_SYSTEME_FERMETURE_VERROU_CADENAS = "verrou_cadenas" +ACCUEIL_SYSTEME_FERMETURE_AUTRE = "autre" +ACCUEIL_SYSTEME_FERMETURE_CHOICES = [ + (ACCUEIL_SYSTEME_FERMETURE_SERRURE_AVEC_CLE, translate_lazy("Serrure avec clé")), + (ACCUEIL_SYSTEME_FERMETURE_ELECTRONIQUE_A_CODE, translate_lazy("Serrure électronique à code")), + (ACCUEIL_SYSTEME_FERMETURE_MECANIQUE_A_CODE, translate_lazy("Serrure mécanique à code")), + (ACCUEIL_SYSTEME_FERMETURE_VERROU_CADENAS, translate_lazy("Verrou pour cadenas personnel")), + (ACCUEIL_SYSTEME_FERMETURE_AUTRE, translate_lazy("Autre")), +] + +ACCUEIL_PRESTATIONS_COMPLEMENTAIRES_SONORISATION = "sonorisation_arbitrage" +ACCUEIL_PRESTATIONS_COMPLEMENTAIRES_SCORE_VISIBLE = "score_visible" +ACCUEIL_PRESTATIONS_COMPLEMENTAIRES_ESPACE_DEPOSE_EQUIPEMENTS = "prestation_espace_depose_equipements" +ACCUEIL_PRESTATIONS_COMPLEMENTAIRES_RESERVATION_PLACE_FACILE_ACCES = "reservation_place_facile_d_acces" +ACCUEIL_PRESTATIONS_COMPLEMENTAIRES_CHOICES = [ + (ACCUEIL_PRESTATIONS_COMPLEMENTAIRES_SONORISATION, translate_lazy("Sonorisation de l'arbitrage possible")), + (ACCUEIL_PRESTATIONS_COMPLEMENTAIRES_SCORE_VISIBLE, translate_lazy("Score visible depuis les places assises")), + ( + ACCUEIL_PRESTATIONS_COMPLEMENTAIRES_ESPACE_DEPOSE_EQUIPEMENTS, + translate_lazy("Espace de dépose d'équipements (fauteuils, déambulateurs...)"), + ), + ( + ACCUEIL_PRESTATIONS_COMPLEMENTAIRES_RESERVATION_PLACE_FACILE_ACCES, + translate_lazy("Réservation possible de places facile d'accès"), + ), +] + +ACCUEIL_PRESENCE_ESPACES_SPECIFIQUES_DEPOSE_EQUIPEMENTS = "presence_espace_depose_equipements" +ACCUEIL_PRESENCE_ESPACES_SPECIFIQUES_REPOS_SENSORIEL = "presence_espace_repos_sensoriel" +ACCUEIL_PRESENCE_ESPACES_SPECIFIQUES_CHIENS_GUIDES = "presence_espace_chiens_guides" +ACCUEIL_PRESENCE_ESPACES_SPECIFIQUES_SALLE_DE_CHANGE = "presence_espace_salle_de_change" +ACCUEIL_PRESENCE_ESPACES_SPECIFIQUES_CHOICES = [ + ( + ACCUEIL_PRESENCE_ESPACES_SPECIFIQUES_DEPOSE_EQUIPEMENTS, + translate_lazy("Espace de dépose d'équipements (fauteuils, déambulateurs...)"), + ), + ( + ACCUEIL_PRESENCE_ESPACES_SPECIFIQUES_REPOS_SENSORIEL, + translate_lazy("Espace de repos sensoriel (hyposensoriel, d'apaisement...)"), + ), + ( + ACCUEIL_PRESENCE_ESPACES_SPECIFIQUES_CHIENS_GUIDES, + translate_lazy("Espace destiné aux chiens guides et d'assistance"), + ), + ( + ACCUEIL_PRESENCE_ESPACES_SPECIFIQUES_SALLE_DE_CHANGE, + translate_lazy("Salle de change destinée aux personnes ayant des handicaps multiples ou complexes"), + ), +] + HANDICAP_AUDITIF = "auditif" HANDICAP_MENTAL = "mental" HANDICAP_MOTEUR = "moteur" @@ -963,6 +1063,33 @@ "free_text": False, "root": False, }, + "stationnement_zone_depose_pmr": { + "type": "boolean", + "nullable": True, + "is_a11y": True, + "label": translate_lazy("Aire de dépose PMR"), + "help_text": mark_safe( + translate_lazy( + "Existe-t-il une zone de dépose PMR pour les grands véhicules adaptés à proximité de l'entrée du gymnase ? (au moins 8m de longueur)" + ) + ), + "help_text_ui": translate_lazy("Présence d'une zone de dépose PMR"), + "help_text_ui_v2": translate_lazy("Présence d'une zone de dépose PMR"), + "help_text_ui_neg": translate_lazy("Pas de zone de dépose PMR"), + "help_text_ui_neg_v2": translate_lazy("Pas de zone de dépose PMR"), + "choices": NULLABLE_BOOLEAN_CHOICES, + "choices_images": ( + ("/static/img/contrib/zone-depose-pmr.png"), + ("/static/img/contrib/no.png"), + ("/static/img/contrib/unknown.png"), + ), + "conditional": "sports_equipment", + "section": SECTION_TRANSPORT, + "nullable_bool": True, + "warn_if": False, + "free_text": False, + "root": True, + }, # Cheminement extérieur "cheminement_ext_presence": { "type": "boolean", @@ -2688,6 +2815,320 @@ "root": True, "excluded_from_completion_rate": True, }, + "accueil_physique": { + "type": "string", + "nullable": True, + "is_a11y": True, + "label": translate_lazy("Accueil physique"), + "help_text": mark_safe( + translate_lazy("Y a-t-il du personnel présent pendant les heures d’ouverture de l'établissement ?") + ), + "help_text_ui": translate_lazy("Présence de personnel pendant les heures d'ouverture"), + "help_text_ui_neg": translate_lazy("Pas de personnel dans l'établisement"), + "choices": ACCUEIL_PHYSIQUE_CHOICES, + "section": SECTION_ACCUEIL, + "nullable_bool": True, + "warn_if": None, + "free_text": False, + "conditional": "sports_equipment", + "root": True, + }, + "accueil_aire_de_jeux": { + "type": "boolean", + "nullable": True, + "is_a11y": True, + "label": translate_lazy("Aire de jeux"), + "help_text": mark_safe(translate_lazy("L'aire de jeux est-elle accessible à une personne en fauteuil ?")), + "help_text_ui": translate_lazy("L'aire de jeux est accessible à une personne en fauteuil"), + "help_text_ui_neg": translate_lazy("L'aire de jeux n'est pas accessible à une personne en fauteuil"), + "choices": NULLABLE_BOOLEAN_CHOICES, + "section": SECTION_ACCUEIL, + "nullable_bool": True, + "warn_if": False, + "free_text": False, + "conditional": "sports_equipment", + "root": True, + }, + "accueil_tribunes": { + "type": "boolean", + "nullable": True, + "is_a11y": True, + "label": translate_lazy("Tribunes"), + "help_text": mark_safe(translate_lazy("Les places destinées aux spectateurs ont-elles un dossier ?")), + "help_text_ui": translate_lazy("Places spectateurs avec dossier"), + "help_text_ui_neg": translate_lazy("Places spectateurs sans dossier"), + "choices": NULLABLE_BOOLEAN_CHOICES, + "section": SECTION_ACCUEIL, + "nullable_bool": True, + "warn_if": False, + "free_text": False, + "conditional": "sports_equipment", + "root": True, + }, + "accueil_tribunes_places": { + "type": "number", + "nullable": True, + "is_a11y": True, + "label": translate_lazy("Places accessibles en tribunes"), + "help_text": mark_safe( + translate_lazy("Combien y a-t-il de places accessibles pour les personnes en fauteuil roulant ?") + ), + "help_text_ui": translate_lazy("Places accessibles pour les personnes en fauteuil roulant"), + "help_text_ui_neg": translate_lazy("Places accessibles pour les personnes en fauteuil roulant"), + "choices": None, + "unit": "place", + "section": SECTION_ACCUEIL, + "nullable_bool": False, + "free_text": False, + "should_display_label": False, + "conditional": "sports_equipment", + "min_value_to_display_children": 1, + "children": ["accueil_tribunes_localisation_places", "accueil_tribunes_places_avec_accompagnants"], + "root": True, + }, + "accueil_tribunes_localisation_places": { + "type": "string", + "nullable": True, + "is_a11y": True, + "help_text": mark_safe(translate_lazy("Où sont elles localisées ?")), + "help_text_ui": translate_lazy("Localisation des places"), + "help_text_ui_neg": translate_lazy("Localisation des places"), + "choices": ACCUEIL_TRIBUNES_PLACES_CHOICES, + "section": SECTION_ACCUEIL, + "nullable_bool": True, + "free_text": False, + "conditional": "sports_equipment", + "should_display_label": False, + "root": False, + }, + "accueil_tribunes_places_avec_accompagnants": { + "type": "number", + "nullable": True, + "is_a11y": True, + "help_text": mark_safe( + translate_lazy( + "Est-ce que des places pour les accompagnants sont proposées à côté des places pour les personnes en fauteuil roulant ?" + ) + ), + "help_text_ui": translate_lazy("Places accompagnant à côté des places pour les personnes en fauteuil roulant"), + "help_text_ui_neg": translate_lazy( + "Pas de place accompagnant à côté des places pour les personnes en fauteuil roulant" + ), + "choices": None, + "unit": "place", + "section": SECTION_ACCUEIL, + "nullable_bool": False, + "free_text": False, + "conditional": "sports_equipment", + "should_display_label": False, + "root": False, + }, + "accueil_vestiaires": { + "type": "boolean", + "nullable": True, + "is_a11y": True, + "label": translate_lazy("Vestiaires"), + "help_text": mark_safe( + translate_lazy( + "Les vestiaires sont-ils accessibles à une personne en fauteuil roulant (cheminement et porte) ?" + ) + ), + "help_text_ui": translate_lazy("Vestiaires accessibles"), + "help_text_ui_neg": translate_lazy( + "Les vestiaires ne sont pas accessibles aux personnes en fauteuil roulant par un passage d'une largeur entre 90 et 110 cm/ supérieure à 110 cm" + ), + "choices": NULLABLE_BOOLEAN_CHOICES, + "section": SECTION_ACCUEIL, + "nullable_bool": True, + "warn_if": False, + "free_text": False, + "conditional": "sports_equipment", + "value_to_display_children": ["True"], + "children": ["accueil_vestiaires_largeur_passage"], + "root": True, + }, + "accueil_vestiaires_largeur_passage": { + "type": "string", + "nullable": True, + "is_a11y": True, + "help_text": mark_safe(translate_lazy("Quelle est la largeur de passage pour se rendre dans les vestiaires ?")), + "help_text_ui": translate_lazy("Largeur du passage d'au moins 90 cm"), + "help_text_ui_neg": translate_lazy("Largeur inférieure à 90 cm"), + "choices": ACCUEIL_VESTIAIRES_LARGEUR_PASSAGE_CHOICES, + "unit": "centimètre", + "section": SECTION_ACCUEIL, + "nullable_bool": True, + "free_text": False, + "conditional": "sports_equipment", + "should_display_label": False, + "root": False, + }, + "accueil_douches_collectives": { + "type": "boolean", + "nullable": True, + "is_a11y": True, + "label": translate_lazy("Douches collectives"), + "help_text": mark_safe(translate_lazy("Présence de douches collectives ?")), + "help_text_ui": translate_lazy("Présence de douches collectives"), + "help_text_ui_neg": translate_lazy("Pas de douches collectives"), + "choices": NULLABLE_BOOLEAN_CHOICES, + "section": SECTION_ACCUEIL, + "nullable_bool": True, + "warn_if": False, + "free_text": False, + "conditional": "sports_equipment", + "value_to_display_children": ["True"], + "children": ["accueil_douches_collectives_adaptees"], + "root": True, + }, + "accueil_douches_collectives_adaptees": { + "type": "boolean", + "nullable": True, + "is_a11y": True, + "help_text": mark_safe( + translate_lazy( + "Les douches sont-elles accessibles pour une personne en fauteuil roulant ? (accès de plain pied, largeur minimale de 90cm, espace de giration)" + ) + ), + "help_text_ui": translate_lazy("Présence de douches collectives accessibles"), + "help_text_ui_neg": translate_lazy("Pas de douches collectives accessibles aux personnes en fauteuil roulant"), + "choices": NULLABLE_BOOLEAN_CHOICES, + "section": SECTION_ACCUEIL, + "nullable_bool": True, + "warn_if": False, + "free_text": False, + "conditional": "sports_equipment", + "should_display_label": False, + "root": False, + }, + "accueil_douches_individuelles": { + "type": "boolean", + "label": translate_lazy("Douches individuelles"), + "nullable": True, + "is_a11y": True, + "help_text": mark_safe(translate_lazy("Présence de cabines de douche individuelles ?")), + "help_text_ui": translate_lazy("Présence de cabines de douche individuelles accessibles"), + "help_text_ui_neg": translate_lazy( + "Pas de cabine de douche individuelles accessibles aux personnes en fauteuil roulant" + ), + "choices": NULLABLE_BOOLEAN_CHOICES, + "section": SECTION_ACCUEIL, + "nullable_bool": True, + "warn_if": False, + "free_text": False, + "conditional": "sports_equipment", + "value_to_display_children": ["True"], + "children": ["accueil_douches_individuelles_adaptees"], + "root": True, + }, + "accueil_douches_individuelles_adaptees": { + "type": "boolean", + "nullable": True, + "is_a11y": True, + "help_text": mark_safe( + translate_lazy( + "Est-ce qu'il y a des cabines de douches accessibles ? (Espace de giration, diamètre de 1m50, espace d'usage, espace derrière la porte.)" + ) + ), + "help_text_ui": translate_lazy("Présence de cabines de douche individuelles accessibles"), + "help_text_ui_neg": translate_lazy( + "Pas de cabines de douche individuelles accessibles aux personnes en fauteuil roulant" + ), + "choices": NULLABLE_BOOLEAN_CHOICES, + "section": SECTION_ACCUEIL, + "nullable_bool": True, + "warn_if": False, + "free_text": False, + "conditional": "sports_equipment", + "should_display_label": False, + "root": False, + }, + "accueil_casiers": { + "type": "boolean", + "nullable": True, + "is_a11y": True, + "label": translate_lazy("Casiers"), + "help_text": mark_safe(translate_lazy("Y a-t-il des casiers mis à disposition du public?")), + "help_text_ui": translate_lazy("Casiers à disposition du public"), + "help_text_ui_neg": translate_lazy("Pas de casiers à disposition du public"), + "choices": NULLABLE_BOOLEAN_CHOICES, + "section": SECTION_ACCUEIL, + "nullable_bool": True, + "warn_if": False, + "free_text": False, + "conditional": "sports_equipment", + "value_to_display_children": ["True"], + "children": ["accueil_casiers_adaptes", "accueil_casiers_fermeture"], + "root": True, + }, + "accueil_casiers_adaptes": { + "type": "boolean", + "nullable": True, + "is_a11y": True, + "help_text": mark_safe( + translate_lazy( + "Le contraste et/ou le relief des casiers et de leur numérotation permettent-ils une bonne lisibilité pour une personne malvoyante ?" + ) + ), + "help_text_ui": translate_lazy("Le contraste et le relief des casiers permettent une bonne lisibilité."), + "help_text_ui_neg": translate_lazy( + "Le contraste et le relief des casiers ne permettent pas une bonne lisibilité." + ), + "choices": NULLABLE_BOOLEAN_CHOICES, + "section": SECTION_ACCUEIL, + "nullable_bool": True, + "warn_if": False, + "free_text": False, + "conditional": "sports_equipment", + "should_display_label": False, + "root": False, + }, + "accueil_casiers_fermeture": { + "type": "array", + "nullable": True, + "is_a11y": True, + "help_text": mark_safe(translate_lazy("Quel est leur système de fermeture ?")), + "help_text_ui": translate_lazy("Système de fermeture présent"), + "help_text_ui_neg": translate_lazy("Système de fermeture absent"), + "choices": ACCUEIL_SYSTEME_FERMETURE_CHOICES, + "section": SECTION_ACCUEIL, + "nullable_bool": False, + "warn_if": False, + "conditional": "sports_equipment", + "should_display_label": False, + "free_text": False, + "root": False, + }, + "accueil_prestations_complementaires": { + "type": "array", + "nullable": True, + "is_a11y": True, + "label": mark_safe(translate_lazy("Prestations complémentaires")), + "help_text_ui": translate_lazy("Prestations complémentaires disponibles"), + "help_text_ui_neg": translate_lazy("Pas de prestations complémentaires"), + "choices": ACCUEIL_PRESTATIONS_COMPLEMENTAIRES_CHOICES, + "section": SECTION_ACCUEIL, + "nullable_bool": False, + "warn_if": False, + "conditional": "sports_equipment", + "free_text": False, + "root": True, + }, + "accueil_presence_espaces_specifiques": { + "type": "array", + "nullable": True, + "is_a11y": True, + "label": mark_safe(translate_lazy("Présence d'espaces spécifiques")), + "help_text_ui": translate_lazy("Présence d'espaces spécifiques"), + "help_text_ui_neg": translate_lazy("Pas d'espaces spécifiques à disposition"), + "choices": ACCUEIL_PRESENCE_ESPACES_SPECIFIQUES_CHOICES, + "section": SECTION_ACCUEIL, + "nullable_bool": False, + "warn_if": False, + "conditional": "sports_equipment", + "free_text": False, + "root": True, + }, # Sanitaires "sanitaires_presence": { "type": "boolean", @@ -2705,7 +3146,7 @@ "free_text": False, "root": True, "value_to_display_children": ["True"], - "children": ["sanitaires_adaptes"], + "children": ["sanitaires_adaptes", "sanitaires_urinoirs"], }, "sanitaires_adaptes": { "type": "boolean", @@ -2726,6 +3167,56 @@ "nullable_bool": True, "warn_if": False, "free_text": False, + "value_to_display_children": ["True"], + "children": ["sanitaires_largeur_porte", "sanitaires_sens_transfert"], + }, + "sanitaires_largeur_porte": { + "type": "string", + "nullable": True, + "is_a11y": True, + "help_text": mark_safe(translate_lazy("Quelle est la largeur de porte des toilettes ?")), + "help_text_ui": translate_lazy("Toilettes adaptées avec une largeur de passage entre"), + "help_text_ui_neg": translate_lazy("Toilettes adaptées avec une largeur de passage entre"), + "choices": SANITAIRES_LARGEUR_PORTE_CHOICES, + "unit": "centimètre", + "section": SECTION_ACCUEIL, + "nullable_bool": True, + "free_text": False, + "conditional": "sports_equipment", + "should_display_label": False, + "root": False, + }, + "sanitaires_sens_transfert": { + "type": "string", + "nullable": True, + "is_a11y": True, + "help_text": mark_safe(translate_lazy("Quel est le sens du transfert ?")), + "help_text_ui": translate_lazy("Sens du transfert"), + "help_text_ui_neg": translate_lazy("Sens du transfert"), + "choices": SANITAIRES_SENS_TRANSFERT_CHOICES, + "unit": "centimètre", + "section": SECTION_ACCUEIL, + "nullable_bool": True, + "free_text": False, + "conditional": "sports_equipment", + "should_display_label": False, + "root": False, + }, + "sanitaires_urinoirs": { + "type": "boolean", + "nullable": True, + "is_a11y": True, + "help_text": translate_lazy("Est ce que les urinoirs sont positionnés à différentes hauteurs ?"), + "help_text_ui": translate_lazy("Urinoirs à différentes hauteurs"), + "help_text_ui_neg": translate_lazy("Les urinoirs sont tous à la même hauteur"), + "choices": NULLABLE_BOOLEAN_CHOICES, + "section": SECTION_ACCUEIL, + "nullable_bool": True, + "warn_if": False, + "free_text": False, + "conditional": "sports_equipment", + "should_display_label": False, + "root": False, }, # Labels "labels": { diff --git a/static/img/contrib/tribunes-places.png b/static/img/contrib/tribunes-places.png new file mode 100644 index 0000000000..3e636ecd2e Binary files /dev/null and b/static/img/contrib/tribunes-places.png differ diff --git a/static/img/contrib/zone-depose-pmr.png b/static/img/contrib/zone-depose-pmr.png new file mode 100644 index 0000000000..fe9a5d585d Binary files /dev/null and b/static/img/contrib/zone-depose-pmr.png differ diff --git a/static/js/ui/ConditionalForm.js b/static/js/ui/ConditionalForm.js index a8bf76c664..d4e15bf0ee 100644 --- a/static/js/ui/ConditionalForm.js +++ b/static/js/ui/ConditionalForm.js @@ -1,8 +1,3 @@ -const INDENTS_LEVEL = { - first: 6, - second: 10, -} - const rules = [ // Transport { @@ -15,13 +10,11 @@ const rules = [ source: 'stationnement_presence', values: ['True'], targets: ['stationnement_pmr'], - indent: INDENTS_LEVEL.first, }, { source: 'stationnement_ext_presence', values: ['True'], targets: ['stationnement_ext_pmr'], - indent: INDENTS_LEVEL.first, }, // Presence d'un extérieur et cheminement { @@ -36,7 +29,6 @@ const rules = [ 'cheminement_ext_retrecissement', 'cheminement_ext_pente_presence', ], - indent: INDENTS_LEVEL.first, }, { source: 'cheminement_ext_plain_pied', @@ -49,38 +41,32 @@ const rules = [ 'cheminement_ext_rampe', 'cheminement_ext_ascenseur', ], - indent: INDENTS_LEVEL.second, }, { source: 'cheminement_ext_ascenseur', values: ['True'], targets: ['cheminement_ext_ascenseur_pmr'], - indent: INDENTS_LEVEL.second, }, { source: 'cheminement_ext_pente_presence', values: ['True'], targets: ['cheminement_ext_pente_degre_difficulte', 'cheminement_ext_pente_longueur'], - indent: INDENTS_LEVEL.second, }, // Entrée { source: 'entree_porte_presence', values: ['True'], targets: ['entree_porte_manoeuvre', 'entree_porte_type', 'entree_vitree'], - indent: INDENTS_LEVEL.first, }, { source: 'entree_vitree', values: ['True'], targets: ['entree_vitree_vitrophanie'], - indent: INDENTS_LEVEL.second, }, { source: 'entree_dispositif_appel', values: ['True'], targets: ['entree_dispositif_appel_type'], - indent: INDENTS_LEVEL.first, }, { source: 'entree_plain_pied', @@ -93,32 +79,27 @@ const rules = [ 'entree_marches_rampe', 'entree_ascenseur', ], - indent: INDENTS_LEVEL.first, }, { source: 'entree_ascenseur', values: ['True'], targets: ['entree_ascenseur_pmr'], - indent: INDENTS_LEVEL.second, }, { source: 'entree_pmr', values: ['True'], targets: ['entree_pmr_informations'], - indent: INDENTS_LEVEL.first, }, // Accueil { source: 'accueil_audiodescription_presence', values: ['True'], targets: ['accueil_audiodescription'], - indent: INDENTS_LEVEL.first, }, { source: 'accueil_equipements_malentendants_presence', values: ['True'], targets: ['accueil_equipements_malentendants'], - indent: INDENTS_LEVEL.first, }, { source: 'accueil_cheminement_plain_pied', @@ -131,7 +112,6 @@ const rules = [ 'accueil_cheminement_rampe', 'accueil_cheminement_ascenseur', ], - indent: INDENTS_LEVEL.first, }, { source: 'accueil_cheminement_ascenseur', @@ -142,20 +122,47 @@ const rules = [ source: 'accueil_ascenseur_etage', values: ['True'], targets: ['accueil_ascenseur_etage_pmr'], - indent: INDENTS_LEVEL.first, }, { source: 'accueil_soignant', values: ['True'], targets: ['accueil_soignant_experience'], - indent: INDENTS_LEVEL.first, + }, + { + source: 'accueil_tribunes_places', + minValue: 1, + targets: ['accueil_tribunes_localisation_places', 'accueil_tribunes_places_avec_accompagnants'], + }, + { + source: 'accueil_vestiaires', + values: ['True'], + targets: ['accueil_vestiaires_largeur_passage'], + }, + { + source: 'accueil_douches_collectives', + values: ['True'], + targets: ['accueil_douches_collectives_adaptees'], + }, + { + source: 'accueil_douches_individuelles', + values: ['True'], + targets: ['accueil_douches_individuelles_adaptees'], + }, + { + source: 'accueil_casiers', + values: ['True'], + targets: ['accueil_casiers_adaptes', 'accueil_casiers_fermeture'], }, // Sanitaires { source: 'sanitaires_presence', values: ['True'], - targets: ['sanitaires_adaptes'], - indent: INDENTS_LEVEL.first, + targets: ['sanitaires_adaptes', 'sanitaires_urinoirs'], + }, + { + source: 'sanitaires_adaptes', + values: ['True'], + targets: ['sanitaires_largeur_porte', 'sanitaires_sens_transfert'], }, // Chambres accessibles { @@ -168,7 +175,6 @@ const rules = [ 'accueil_chambre_sanitaires_barre_appui', 'accueil_chambre_sanitaires_espace_usage', ], - indent: INDENTS_LEVEL.first, }, // Labels // TODO @@ -178,20 +184,17 @@ const rules = [ source: 'user_type', values: ['gestionnaire', 'admin'], targets: ['registre_url'], - indent: INDENTS_LEVEL.first, }, // b. afficher conformité si administration { source: 'user_type', values: ['admin'], targets: ['conformite'], - indent: INDENTS_LEVEL.first, }, { source: 'user_type', values: ['gestionnaire'], targets: ['rpa_exemption'], - indent: INDENTS_LEVEL.first, }, ] diff --git a/templates/contrib/3-transport.html b/templates/contrib/3-transport.html index 8285b68930..1ab42c1422 100644 --- a/templates/contrib/3-transport.html +++ b/templates/contrib/3-transport.html @@ -51,5 +51,8 @@ {% include "fields/boolradio.html" with field=form.stationnement_ext_pmr key="stationnement_ext_pmr" inline=True %} +
+ {{ form.cheminement_ext_signaletique_exterieure.label }}
+