diff --git a/cpmonitor/admin.py b/cpmonitor/admin.py index 70dbd7f5..e9a8df8f 100644 --- a/cpmonitor/admin.py +++ b/cpmonitor/admin.py @@ -14,17 +14,16 @@ from treebeard.forms import movenodeform_factory, MoveNodeForm from cpmonitor.views import SelectCityView, CapEditView - from . import rules, utils from .models import ( Chart, City, Task, - CapChecklist, AdministrationChecklist, LocalGroup, Invitation, EnergyPlanChecklist, + CapChecklist, ) @@ -81,32 +80,6 @@ class ChartInline(ObjectPermissionsModelAdminMixin, admin.StackedInline): } -class CapChecklistInline(ObjectPermissionsModelAdminMixin, admin.StackedInline): - model = CapChecklist - - formfield_overrides = { - models.TextField: {"widget": AdminMartorWidget}, - } - - -class AdministrationChecklistInline( - ObjectPermissionsModelAdminMixin, admin.StackedInline -): - model = AdministrationChecklist - - formfield_overrides = { - models.TextField: {"widget": AdminMartorWidget}, - } - - -class EnergyPlanChecklistInline(ObjectPermissionsModelAdminMixin, admin.StackedInline): - model = EnergyPlanChecklist - - formfield_overrides = { - models.TextField: {"widget": AdminMartorWidget}, - } - - class LocalGroupInline(ObjectPermissionsModelAdminMixin, admin.StackedInline): model = LocalGroup @@ -143,6 +116,28 @@ def invitation_link(self, invitation: Invitation): ) +class ChecklistAdmin(ObjectPermissionsModelAdminMixin, admin.ModelAdmin): + formfield_overrides = { + models.TextField: {"widget": AdminMartorWidget}, + } + + save_on_top = True + + def get_readonly_fields(self, request, obj=None): + if obj: + return ("city",) + else: + return () + + def get_changeform_initial_data(self, request: HttpRequest): + query_string = self.get_preserved_filters(request) + filters = QueryDict(query_string).get("_changelist_filters") + city_id = QueryDict(filters).get(_city_filter_query) + return { + "city": city_id, + } + + class CityAdmin(ObjectPermissionsModelAdminMixin, admin.ModelAdmin): # ------ change list page ------ list_display = ("zipcode", "name", "teaser", "edit_tasks") @@ -191,9 +186,6 @@ def get_readonly_fields(self, request: HttpRequest, obj=None) -> Sequence[str]: inlines = [ ChartInline, LocalGroupInline, - CapChecklistInline, - AdministrationChecklistInline, - EnergyPlanChecklistInline, InvitationInline, ] @@ -399,3 +391,6 @@ def add_view(self, request, form_url="", extra_context=None): admin.site.register(City, CityAdmin) admin.site.register(Task, TaskAdmin) +admin.site.register(CapChecklist, ChecklistAdmin) +admin.site.register(AdministrationChecklist, ChecklistAdmin) +admin.site.register(EnergyPlanChecklist, ChecklistAdmin) diff --git a/cpmonitor/migrations/0043_alter_administrationchecklist_options_and_more.py b/cpmonitor/migrations/0043_alter_administrationchecklist_options_and_more.py new file mode 100644 index 00000000..abf4d5ec --- /dev/null +++ b/cpmonitor/migrations/0043_alter_administrationchecklist_options_and_more.py @@ -0,0 +1,33 @@ +# Generated by Django 4.2.9 on 2024-04-10 16:24 + +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [ + ("cpmonitor", "0042_localgroup_logo_alter_localgroup_featured_image"), + ] + + operations = [ + migrations.AlterModelOptions( + name="administrationchecklist", + options={ + "verbose_name": "Checkliste zu Verwaltungsstrukturen", + "verbose_name_plural": "Checklisten zu Verwaltungsstrukturen", + }, + ), + migrations.AlterModelOptions( + name="capchecklist", + options={ + "verbose_name": "Checkliste zum KAP", + "verbose_name_plural": "Checklisten zum KAP", + }, + ), + migrations.AlterModelOptions( + name="energyplanchecklist", + options={ + "verbose_name": "Checkliste zur Wärmeplanung", + "verbose_name_plural": "Checklisten zur Wärmeplanung", + }, + ), + ] diff --git a/cpmonitor/migrations/0044_alter_administrationchecklist_climate_protection_management_exists_rationale_and_more.py b/cpmonitor/migrations/0044_alter_administrationchecklist_climate_protection_management_exists_rationale_and_more.py new file mode 100644 index 00000000..78860d9c --- /dev/null +++ b/cpmonitor/migrations/0044_alter_administrationchecklist_climate_protection_management_exists_rationale_and_more.py @@ -0,0 +1,102 @@ +# Generated by Django 4.2.9 on 2024-04-10 19:34 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("cpmonitor", "0043_alter_administrationchecklist_options_and_more"), + ] + + operations = [ + migrations.AlterField( + model_name="administrationchecklist", + name="climate_protection_management_exists_rationale", + field=models.TextField(blank=True, verbose_name="Begründung"), + ), + migrations.AlterField( + model_name="administrationchecklist", + name="climate_protection_monitoring_exists_rationale", + field=models.TextField(blank=True, verbose_name="Begründung"), + ), + migrations.AlterField( + model_name="administrationchecklist", + name="climate_relevance_check_exists_rationale", + field=models.TextField(blank=True, verbose_name="Begründung"), + ), + migrations.AlterField( + model_name="administrationchecklist", + name="guidelines_for_sustainable_procurement_exists_rationale", + field=models.TextField(blank=True, verbose_name="Begründung"), + ), + migrations.AlterField( + model_name="administrationchecklist", + name="intersectoral_concepts_exists_rationale", + field=models.TextField(blank=True, verbose_name="Begründung"), + ), + migrations.AlterField( + model_name="administrationchecklist", + name="municipal_office_for_funding_management_exists_rationale", + field=models.TextField(blank=True, verbose_name="Begründung"), + ), + migrations.AlterField( + model_name="administrationchecklist", + name="public_relation_with_local_actors_exists_rationale", + field=models.TextField(blank=True, verbose_name="Begründung"), + ), + migrations.AlterField( + model_name="capchecklist", + name="annual_costs_are_specified_rationale", + field=models.TextField(blank=True, verbose_name="Begründung"), + ), + migrations.AlterField( + model_name="capchecklist", + name="annual_reduction_of_emissions_can_be_predicted_rationale", + field=models.TextField(blank=True, verbose_name="Begründung"), + ), + migrations.AlterField( + model_name="capchecklist", + name="based_on_remaining_co2_budget_rationale", + field=models.TextField(blank=True, verbose_name="Begründung"), + ), + migrations.AlterField( + model_name="capchecklist", + name="cap_exists_rationale", + field=models.TextField(blank=True, verbose_name="Begründung"), + ), + migrations.AlterField( + model_name="capchecklist", + name="concept_for_participation_specified_rationale", + field=models.TextField(blank=True, verbose_name="Begründung"), + ), + migrations.AlterField( + model_name="capchecklist", + name="scenario_for_business_as_usual_exists_rationale", + field=models.TextField(blank=True, verbose_name="Begründung"), + ), + migrations.AlterField( + model_name="capchecklist", + name="scenario_for_climate_neutrality_till_2035_exists_rationale", + field=models.TextField(blank=True, verbose_name="Begründung"), + ), + migrations.AlterField( + model_name="capchecklist", + name="sectors_of_climate_vision_used_rationale", + field=models.TextField(blank=True, verbose_name="Begründung"), + ), + migrations.AlterField( + model_name="capchecklist", + name="target_date_exists_rationale", + field=models.TextField(blank=True, verbose_name="Begründung"), + ), + migrations.AlterField( + model_name="capchecklist", + name="tasks_are_planned_yearly_rationale", + field=models.TextField(blank=True, verbose_name="Begründung"), + ), + migrations.AlterField( + model_name="capchecklist", + name="tasks_have_responsible_entity_rationale", + field=models.TextField(blank=True, verbose_name="Begründung"), + ), + ] diff --git a/cpmonitor/migrations/0045_alter_administrationchecklist_city_and_more.py b/cpmonitor/migrations/0045_alter_administrationchecklist_city_and_more.py new file mode 100644 index 00000000..fc0ec737 --- /dev/null +++ b/cpmonitor/migrations/0045_alter_administrationchecklist_city_and_more.py @@ -0,0 +1,46 @@ +# Generated by Django 4.2.9 on 2024-04-12 07:51 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + dependencies = [ + ( + "cpmonitor", + "0044_alter_administrationchecklist_climate_protection_management_exists_rationale_and_more", + ), + ] + + operations = [ + migrations.AlterField( + model_name="administrationchecklist", + name="city", + field=models.OneToOneField( + on_delete=django.db.models.deletion.PROTECT, + related_name="administration_checklist", + to="cpmonitor.city", + verbose_name="Stadt", + ), + ), + migrations.AlterField( + model_name="capchecklist", + name="city", + field=models.OneToOneField( + on_delete=django.db.models.deletion.PROTECT, + related_name="cap_checklist", + to="cpmonitor.city", + verbose_name="Stadt", + ), + ), + migrations.AlterField( + model_name="energyplanchecklist", + name="city", + field=models.OneToOneField( + on_delete=django.db.models.deletion.PROTECT, + related_name="energy_plan_checklist", + to="cpmonitor.city", + verbose_name="Stadt", + ), + ), + ] diff --git a/cpmonitor/models.py b/cpmonitor/models.py index 966db8a5..34bc4ed3 100644 --- a/cpmonitor/models.py +++ b/cpmonitor/models.py @@ -221,10 +221,17 @@ def save(self, *args, **kwargs): class CapChecklist(models.Model): class Meta: - verbose_name = "KAP Checkliste" + verbose_name = "Checkliste zum KAP" + verbose_name_plural = "Checklisten zum KAP" + + def __str__(self): + return "Checkliste zum KAP für " + self.city.name city = models.OneToOneField( - City, on_delete=models.PROTECT, related_name="cap_checklist" + City, + on_delete=models.PROTECT, + related_name="cap_checklist", + verbose_name="Stadt", ) cap_exists = models.BooleanField( @@ -239,7 +246,7 @@ class Meta: "Bereichen der Kommune von Bedeutung.", ) cap_exists_rationale = models.TextField( - "Begründung zu: Gibt es einen Klima-Aktionsplan?", + "Begründung", blank=True, ) target_date_exists = models.BooleanField( @@ -252,7 +259,7 @@ class Meta: "eingespart werden.", ) target_date_exists_rationale = models.TextField( - "Begründung zu: Ist im Klima-Aktionsplan ein Zieljahr der Klimaneutralität hinterlegt", + "Begründung", blank=True, ) based_on_remaining_co2_budget = models.BooleanField( @@ -265,7 +272,7 @@ class Meta: "überschreiten darf.", ) based_on_remaining_co2_budget_rationale = models.TextField( - "Begründung zu: Sind die Einsparziele im Klima-Aktionsplan auf Grundlage des Restbudgets berechnet?", + "Begründung", blank=True, ) sectors_of_climate_vision_used = models.BooleanField( @@ -280,7 +287,7 @@ class Meta: "muss und vor allem in den fehlenden Sektoren trotzdem Maßnahmen entwickelt werden sollten.", ) sectors_of_climate_vision_used_rationale = models.TextField( - "Begründung zu: Bilanziert der Klima-Aktionsplan vollständig, zum Beispiel in den Sektoren der Klimavision?", + "Begründung", blank=True, ) scenario_for_climate_neutrality_till_2035_exists = models.BooleanField( @@ -292,7 +299,7 @@ class Meta: "Oft ist im KAP ein weiteres Szenario mit einem anderen Zieljahr hinterlegt zum Bsp. 2040 oder 2045", ) scenario_for_climate_neutrality_till_2035_exists_rationale = models.TextField( - "Begründung zu: Enthält der Klima-Aktionsplan ein Szenario mit dem Ziel Klimaneutralität bis 2035?", + "Begründung", blank=True, ) scenario_for_business_as_usual_exists = models.BooleanField( @@ -304,7 +311,7 @@ class Meta: "Einfluss auf kommunale Emissionen haben (Bsp: veränderter Bundesdeutscher Strommix).", ) scenario_for_business_as_usual_exists_rationale = models.TextField( - "Begründung zu: Ist ein Trendszenario hinterlegt?", + "Begründung", blank=True, ) annual_costs_are_specified = models.BooleanField( @@ -317,7 +324,7 @@ class Meta: "ermittelt werden.", ) annual_costs_are_specified_rationale = models.TextField( - "Begründung zu: Sind die jährlichen Kosten und der jährliche Personalbedarf der Maßnahmen ausgewiesen?", + "Begründung", blank=True, ) tasks_are_planned_yearly = models.BooleanField( @@ -332,7 +339,7 @@ class Meta: "den kleinen Emissionsquellen Maßnahmen ergriffen werden.", ) tasks_are_planned_yearly_rationale = models.TextField( - "Begründung zu: Haben die Maßnahmen eine jahresscharfe Planung?", + "Begründung", blank=True, ) tasks_have_responsible_entity = models.BooleanField( @@ -344,7 +351,7 @@ class Meta: "die kommunale Tochter oder sogar die zuständige Sachbearbeitung genannt werden.", ) tasks_have_responsible_entity_rationale = models.TextField( - "Begründung zu: Sind verantwortliche Personen/Fachbereiche/kommunale Gesellschaften für alle Maßnahmen hinterlegt?", + "Begründung", blank=True, ) annual_reduction_of_emissions_can_be_predicted = models.BooleanField( @@ -356,7 +363,7 @@ class Meta: "wird der Weg zur Treibhausgasneutralität klar erkennbar und zu kompensierende Emissionen sichtbar.", ) annual_reduction_of_emissions_can_be_predicted_rationale = models.TextField( - "Begründung zu: Wird anhand der Maßnahmen ein jährlicher Reduktionspfad des Energiebedarfs und der THG-Emissionen ersichtlich?", + "Begründung", blank=True, ) concept_for_participation_specified = models.BooleanField( @@ -368,17 +375,24 @@ class Meta: "z.B. kommunale Unternehmen oder Vereine).", ) concept_for_participation_specified_rationale = models.TextField( - "Begründung zu: Gibt es ein gutes Konzept zur Akteur:innenbeteiligung?", + "Begründung", blank=True, ) class AdministrationChecklist(models.Model): class Meta: - verbose_name = "Verwaltungsstrukturen Checkliste" + verbose_name = "Checkliste zu Verwaltungsstrukturen" + verbose_name_plural = "Checklisten zu Verwaltungsstrukturen" + + def __str__(self): + return "Checkliste zu Verwaltungsstrukturen für " + self.city.name city = models.OneToOneField( - City, on_delete=models.PROTECT, related_name="administration_checklist" + City, + on_delete=models.PROTECT, + related_name="administration_checklist", + verbose_name="Stadt", ) climate_protection_management_exists = models.BooleanField( @@ -392,8 +406,7 @@ class Meta: "unterstellt ist, sondern selber ein Fachdienst.", ) climate_protection_management_exists_rationale = models.TextField( - "Begründung zu: Gibt es ein Klimaschutzmanagement, das befugt ist, Entscheidungen zu treffen und" - " über Haushaltsmittel verfügt?", + "Begründung", blank=True, ) climate_relevance_check_exists = models.BooleanField( @@ -412,7 +425,7 @@ class Meta: "Beschlüsse werden somit bereits während der Erstellung durch die Sachbearbeiter:innen in den Fachbereichen auf ihre Klimarelevanz hin (vor-)bewertet und Aspekte des Klimaschutzes sind automatisch integraler Bestandteil jeder Beschlussfassung. Klimafolgen werden somit transparent, Politiker:innen können fundierter entscheiden. Langfristig baut die Kommune Kompetenzen auf, um die Auswirkung auf das Klima bei allen relevanten Entscheidungen zu berücksichtigen.", ) climate_relevance_check_exists_rationale = models.TextField( - "Begründung zu: Klimarelevanzprüfung: werden alle Beschlüsse von Verwaltung und Politik auf die Auswirkungen auf das Klima geprüft?", + "Begründung", blank=True, ) climate_protection_monitoring_exists = models.BooleanField( @@ -426,7 +439,7 @@ class Meta: "behalten.", ) climate_protection_monitoring_exists_rationale = models.TextField( - "Begründung zu: Gibt es ein Monitoring von Kimaschutzmaßnahmen?", + "Begründung", blank=True, ) intersectoral_concepts_exists = models.BooleanField( @@ -440,7 +453,7 @@ class Meta: "das Kimaschutz eine zentrale Rolle spielt.", ) intersectoral_concepts_exists_rationale = models.TextField( - "Begründung zu: Beziehen (sektorenübergreifende) Konzepte und Planungspapiere Klimaschutz mit ein?", + "Begründung", blank=True, ) guidelines_for_sustainable_procurement_exists = models.BooleanField( @@ -455,7 +468,7 @@ class Meta: "Beschaffungsleitfaden als Organisationsziel definiert werden.", ) guidelines_for_sustainable_procurement_exists_rationale = models.TextField( - "Begründung zu: Gibt es Richtlinien für ein nachhaltiges Beschaffungswesen?", + "Begründung", blank=True, ) municipal_office_for_funding_management_exists = models.BooleanField( @@ -466,7 +479,7 @@ class Meta: "und dafür sorgen, dass effizient an Klimaschutz gearbeitet werden kann.", ) municipal_office_for_funding_management_exists_rationale = models.TextField( - "Begründung zu: Gibt es eine eigene kommunale Stelle für Fördermittelmanagement (unter anderem Beantragung etc. für den Klimaschutz)?", + "Begründung", blank=True, ) public_relation_with_local_actors_exists = models.BooleanField( @@ -476,17 +489,24 @@ class Meta: "Lokalpolitik beraten. Um politischen Einfluss auszuüben sollten diese regelmäßig tagen.", ) public_relation_with_local_actors_exists_rationale = models.TextField( - "Begründung zu: Gibt es einen Klimabeirat/Klimarat/Bürger:innenrat? Ist so ein Gremium in der Kommune eingerichtet und tagt regelmäßig?", + "Begründung", blank=True, ) class EnergyPlanChecklist(models.Model): class Meta: - verbose_name = "Wärmeplanung Checkliste" + verbose_name = "Checkliste zur Wärmeplanung" + verbose_name_plural = "Checklisten zur Wärmeplanung" + + def __str__(self): + return "Checkliste zur Wärmeplanung für " + self.city.name city = models.OneToOneField( - City, on_delete=models.PROTECT, related_name="energy_plan_checklist" + City, + on_delete=models.PROTECT, + related_name="energy_plan_checklist", + verbose_name="Stadt", ) energy_plan_exists = models.BooleanField( diff --git a/cpmonitor/static/css/admin.css b/cpmonitor/static/css/admin.css index d48e3050..60fd7f03 100644 --- a/cpmonitor/static/css/admin.css +++ b/cpmonitor/static/css/admin.css @@ -8,6 +8,11 @@ body, height: calc(80% - 100px); } +.admin-h2 { + padding-top: 20px; + margin-bottom: 5px; +} + .main { background: white; } diff --git a/cpmonitor/static/css/admin.css.map b/cpmonitor/static/css/admin.css.map index e0f3c0c0..a8f5c2dd 100644 --- a/cpmonitor/static/css/admin.css.map +++ b/cpmonitor/static/css/admin.css.map @@ -1 +1 @@ -{"version":3,"sourceRoot":"","sources":["admin.scss"],"names":[],"mappings":"AAEA;AAAA;AAAA;EAGE;;;AAIA;EACE;;;AAIJ;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;;AAIJ;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;;;AAKF;EACE;;;AAIJ;EACE;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAyBF;EACE;;;AAGA;EACE,aAzBQ;;;AAwBV;EACE,aAzBQ;;;AAwBV;EACE,aAzBQ;;;AAwBV;EACE,aAzBQ;;;AAwBV;EACE,aAzBQ;;;AAwBV;EACE,aAzBQ","file":"admin.css"} \ No newline at end of file +{"version":3,"sourceRoot":"","sources":["admin.scss"],"names":[],"mappings":"AAEA;AAAA;AAAA;EAGE;;;AAIA;EACE;;;AAIJ;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;;AAIJ;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;;;AAKF;EACE;;;AAIJ;EACE;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAyBF;EACE;;;AAGA;EACE,aAzBQ;;;AAwBV;EACE,aAzBQ;;;AAwBV;EACE,aAzBQ;;;AAwBV;EACE,aAzBQ;;;AAwBV;EACE,aAzBQ;;;AAwBV;EACE,aAzBQ","file":"admin.css"} \ No newline at end of file diff --git a/cpmonitor/static/css/admin.scss b/cpmonitor/static/css/admin.scss index 635c4e60..e087c942 100644 --- a/cpmonitor/static/css/admin.scss +++ b/cpmonitor/static/css/admin.scss @@ -12,6 +12,11 @@ body, } } +.admin-h2 { + padding-top: 20px; + margin-bottom: 5px; +} + .main { background: white; } diff --git a/cpmonitor/templates/admin/admin-cap.html b/cpmonitor/templates/admin/admin-cap.html index a2fb1982..b28092c6 100644 --- a/cpmonitor/templates/admin/admin-cap.html +++ b/cpmonitor/templates/admin/admin-cap.html @@ -8,6 +8,44 @@ {% endblock extrahead %} {% block content %} {% csrf_token %} +

Checklisten

+
+ Checkliste zum KAP + {% if cap_checklist_id == None %} + hinzufügen + {% else %} + editieren + löschen + {% endif %} +
+
+ Checkliste zu Verwaltungsstrukturen + {% if administration_checklist_id == None %} + hinzufügen + {% else %} + editieren + löschen + {% endif %} +
+
+ Checkliste zur Wärmeplanung + {% if energy_plan_checklist_id == None %} + hinzufügen + {% else %} + editieren + löschen + {% endif %} +
+

KAP Board

Handlungsfeld hinzufügen diff --git a/cpmonitor/tests/permissions_test.py b/cpmonitor/tests/permissions_test.py index 080c2c8b..eb2bc076 100644 --- a/cpmonitor/tests/permissions_test.py +++ b/cpmonitor/tests/permissions_test.py @@ -242,14 +242,10 @@ def test_city_editor_should_be_allowed_to_modify_inlines( formsets = response.context["inline_admin_formsets"] verbose_names = list(map(lambda formset: formset.opts.verbose_name, formsets)) - # This will fail when new inlines are added. - # If that happens, please extend the list below, accordingly. - assert len(formsets) == 5 + assert "Diagramm" in verbose_names assert "Lokalgruppe" in verbose_names - assert "KAP Checkliste" in verbose_names - assert "Verwaltungsstrukturen Checkliste" in verbose_names - assert "Wärmeplanung Checkliste" in verbose_names + assert len(formsets) == 2 for formset in formsets: assert formset.has_view_permission @@ -266,15 +262,11 @@ def test_city_admin_should_be_allowed_to_modify_inlines( formsets = response.context["inline_admin_formsets"] verbose_names = list(map(lambda formset: formset.opts.verbose_name, formsets)) - # This will fail when new inlines are added. - # If that happens, please extend the list below, accordingly. - assert len(formsets) == 6 + assert "Diagramm" in verbose_names assert "Lokalgruppe" in verbose_names - assert "KAP Checkliste" in verbose_names - assert "Verwaltungsstrukturen Checkliste" in verbose_names - assert "Wärmeplanung Checkliste" in verbose_names assert "Einladungslink" in verbose_names + assert len(formsets) == 3 for formset in formsets: assert formset.has_view_permission @@ -292,15 +284,11 @@ def test_site_admin_should_be_allowed_to_modify_inlines( formsets = response.context["inline_admin_formsets"] verbose_names = list(map(lambda formset: formset.opts.verbose_name, formsets)) - # This will fail when new inlines are added. - # If that happens, please extend the list below, accordingly. + assert "Diagramm" in verbose_names assert "Lokalgruppe" in verbose_names - assert "KAP Checkliste" in verbose_names - assert "Verwaltungsstrukturen Checkliste" in verbose_names assert "Einladungslink" in verbose_names - assert "Wärmeplanung Checkliste" in verbose_names - assert len(formsets) == 6 + assert len(formsets) == 3 for formset in formsets: assert formset.has_view_permission diff --git a/cpmonitor/views.py b/cpmonitor/views.py index 6c35e1b8..0e97689a 100644 --- a/cpmonitor/views.py +++ b/cpmonitor/views.py @@ -143,6 +143,13 @@ def _get_task_groups(city): return groups +# def _get_cap_checklist(city) -> CapChecklist | None: +# cap_checklists = CapChecklist.objects.all().filter(city=city) +# if len(cap_checklists) > 0: +# return cap_checklists[0] +# return None + + def _get_cities(request, slug=None): try: cities = City.objects.all() @@ -180,13 +187,13 @@ def city_view(request, city_slug): _calculate_summary(request, city) - cap_checklist = _get_cap_checklist(city) + cap_checklist = _get_cap_checklist_items(city) cap_checklist_exists = len(cap_checklist) > 0 - administration_checklist = _get_administration_checklist(city) + administration_checklist = _get_administration_checklist_items(city) administration_checklist_exists = len(administration_checklist) > 0 - energy_plan_checklist = _get_energy_plan_checklist(city) + energy_plan_checklist = _get_energy_plan_checklist_items(city) energy_plan_checklist_exists = len(energy_plan_checklist) > 0 breadcrumbs = _get_breadcrumbs( @@ -292,17 +299,23 @@ def cap_checklist_view(request, city_slug): { "breadcrumbs": breadcrumbs, "city": city, - "cap_checklist": _get_cap_checklist(city), + "cap_checklist": _get_cap_checklist_items(city), "local_group": getattr(city, "local_group", None), } ) return render(request, "cap_checklist.html", context) -def _get_cap_checklist(city) -> list: +def _get_cap_checklist(city) -> CapChecklist | None: try: - checklist = city.cap_checklist + return city.cap_checklist except CapChecklist.DoesNotExist: + return None + + +def _get_cap_checklist_items(city) -> list: + checklist = _get_cap_checklist(city) + if checklist is None: return [] return _as_formatted_checklist(checklist) @@ -326,7 +339,7 @@ def administration_checklist_view(request, city_slug): { "breadcrumbs": breadcrumbs, "city": city, - "administration_checklist": _get_administration_checklist(city), + "administration_checklist": _get_administration_checklist_items(city), "local_group": getattr(city, "local_group", None), } ) @@ -334,10 +347,16 @@ def administration_checklist_view(request, city_slug): return render(request, "administration_checklist.html", context) -def _get_administration_checklist(city) -> list: +def _get_administration_checklist(city) -> AdministrationChecklist | None: try: - checklist = city.administration_checklist + return city.administration_checklist except AdministrationChecklist.DoesNotExist: + return None + + +def _get_administration_checklist_items(city) -> list: + checklist = _get_administration_checklist(city) + if checklist is None: return [] return _as_formatted_checklist(checklist) @@ -361,7 +380,7 @@ def energy_plan_checklist_view(request, city_slug): { "breadcrumbs": breadcrumbs, "city": city, - "energy_plan_checklist": _get_energy_plan_checklist(city), + "energy_plan_checklist": _get_energy_plan_checklist_items(city), "local_group": getattr(city, "local_group", None), } ) @@ -369,10 +388,16 @@ def energy_plan_checklist_view(request, city_slug): return render(request, "energy_plan_checklist.html", context) -def _get_energy_plan_checklist(city) -> list: +def _get_energy_plan_checklist(city) -> EnergyPlanChecklist | None: try: - checklist = city.energy_plan_checklist + return city.energy_plan_checklist except EnergyPlanChecklist.DoesNotExist: + return None + + +def _get_energy_plan_checklist_items(city) -> list: + checklist = _get_energy_plan_checklist(city) + if checklist is None: return [] return _as_formatted_checklist(checklist) @@ -682,10 +707,23 @@ class CapEditView(DetailView, admin.ModelAdmin): template_name = "admin/admin-cap.html" def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) groups = _get_task_groups(self.object) + cap_checklist = _get_cap_checklist(self.object) + administration_checklist = _get_administration_checklist(self.object) + energy_plan_checklist = _get_energy_plan_checklist(self.object) + + context = super().get_context_data(**kwargs) context["title"] = self.object.name context["city_id"] = str(self.object.pk) + context["cap_checklist_id"] = ( + cap_checklist.pk if cap_checklist != None else None + ) + context["administration_checklist_id"] = ( + administration_checklist.pk if administration_checklist != None else None + ) + context["energy_plan_checklist_id"] = ( + energy_plan_checklist.pk if energy_plan_checklist != None else None + ) context["groups"] = groups return context diff --git a/e2e_tests/test_admin.py b/e2e_tests/test_admin.py index 898e9be6..93a64a5e 100644 --- a/e2e_tests/test_admin.py +++ b/e2e_tests/test_admin.py @@ -303,3 +303,41 @@ def test_should_move_the_task_below_the_chosen_task_when_moving_a_task_on_anothe expect(dragged_task.locator("//parent::div").get_by_role("img")).to_have_class( "inline marginleft-1" ) + + +def test_should_add_the_checklist_to_the_respective_city_when_clicking_add_new_cap_checklist( + live_server, page: Page +): + # when + admin_login(live_server.url, page) + go_to_cap_edit_board(page, "Ohnenix") + page.get_by_text("Checkliste zum KAP").get_by_role( + "link", name="hinzufügen" + ).click() + + # then + expect(page.get_by_label("Stadt")).to_contain_text("Ohnenix") + page.get_by_label("Gibt es einen Klima-Aktionsplan").check() + page.get_by_role("button", name="Sichern", exact=True).first.click() + + +def test_should_show_the_checkist_add_button_when_a_checklist_was_deleted( + live_server, page: Page +): + # when + admin_login(live_server.url, page) + go_to_cap_edit_board(page, "Beispielstadt") + page.get_by_text("Checkliste zum KAP").get_by_role("link", name="löschen").click() + page.get_by_text("Ja, ich bin sicher").click() + + # then + go_to_cap_edit_board(page, "Beispielstadt") + expect( + page.get_by_text("Checkliste zum KAP").get_by_role("link", name="löschen") + ).not_to_be_visible() + expect( + page.get_by_text("Checkliste zum KAP").get_by_role("link", name="editieren") + ).not_to_be_visible() + expect( + page.get_by_text("Checkliste zum KAP").get_by_role("link", name="hinzufügen") + ).to_be_visible()