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 %} +