internlm/SWE-Fixer-Train-110K huggingface dataset:
Repository: liqd/a4-meinberlin

Issue:
use list markup for lists of elements
Part of the BITV-Test: "1.3.1b HTML-Strukturelemente für Listen"
----
- [x] list of blueprints
- [x] list of projects

unsure:
- [ ] list of questions in poll contents
- [ ] list of choices in poll contents 


Discussion:

Resolving patch:
diff --git a/apps/contrib/templatetags/contrib_tags.py b/apps/contrib/templatetags/contrib_tags.py
--- a/apps/contrib/templatetags/contrib_tags.py
+++ b/apps/contrib/templatetags/contrib_tags.py
@@ -27,4 +27,4 @@ def filter_has_perm(perm, user, objects):
         # will be returned. This is taken from rules.templatetags.has_perm.
         return objects
     else:
-        return (obj for obj in objects if user.has_perm(perm, obj))
+        return [obj for obj in objects if user.has_perm(perm, obj)]


Test patch:
Repository: liqd/a4-meinberlin

Issue:
Use predicates to prevent access to items if no phase has started yet
With topicprio and polls items are created before any phase has been started.
On the project_detail page a according message and no content is shown.
This is checked via the `PhaseDispatchMixin`.
As for items it is not possible to use the `PhaseDispatchMixin` a new
predicate has been introduced which tests if a phase has been started
yet. Be aware, that this does not imply an active phase.
If no phase has been started yet access to the items should be prevented. 
But if participation did finish the items should still be available.
See 362a8ce6a79888b2cc1dbdeb789107a156c5d15f for reference. 


Discussion:

Resolving patch:
diff --git a/apps/polls/rules.py b/apps/polls/rules.py
--- a/apps/polls/rules.py
+++ b/apps/polls/rules.py
@@ -2,6 +2,7 @@
 from rules.predicates import is_superuser
 
 from adhocracy4.modules import predicates as module_predicates
+from apps.contrib import predicates as contrib_predicates
 
 from . import models
 
@@ -12,7 +13,9 @@
 
 rules.add_perm(
     'meinberlin_polls.view_poll',
-    module_predicates.is_public_context
+    (module_predicates.is_project_admin |
+     (module_predicates.is_allowed_view_item &
+      contrib_predicates.has_context_started))
 )
 
 rules.add_perm(
diff --git a/apps/topicprio/rules.py b/apps/topicprio/rules.py
--- a/apps/topicprio/rules.py
+++ b/apps/topicprio/rules.py
@@ -1,6 +1,7 @@
 import rules
 
 from adhocracy4.modules import predicates as module_predicates
+from apps.contrib import predicates as contrib_predicates
 
 rules.add_perm(
     'meinberlin_topicprio.add_topic',
@@ -12,6 +13,13 @@
     module_predicates.is_project_admin
 )
 
+rules.add_perm(
+    'meinberlin_topicprio.view_topic',
+    (module_predicates.is_project_admin |
+     (module_predicates.is_allowed_view_item &
+      contrib_predicates.has_context_started))
+)
+
 rules.add_perm(
     'meinberlin_topicprio.rate_topic',
     module_predicates.is_allowed_rate_item


Test patch:
Repository: liqd/a4-meinberlin

Issue:
Making messages for translation has system dependent output
When calling `make makemessages` the output of the generated "djangojs.po" is dependent on the system configuration. 
For example in my case it inserts messages from:
"adhocracy4/node_modules/ajv/dist/regenerator.min.js"  and
"adhocracy4/node_modules/js-yaml/dist/js-yaml.min.js"


Discussion:

Resolving patch:
diff --git a/apps/contrib/management/commands/makemessages.py b/apps/contrib/management/commands/makemessages.py
--- a/apps/contrib/management/commands/makemessages.py
+++ b/apps/contrib/management/commands/makemessages.py
@@ -21,9 +21,9 @@ def handle(self, *args, **options):
         return super().handle(*args, **options)
 
     def find_files(self, root):
-        a4js_paths = super().find_files(
-            path.join(settings.BASE_DIR, 'node_modules', 'adhocracy4')
-        )
+        a4js_paths = super().find_files(path.join(
+            settings.BASE_DIR, 'node_modules', 'adhocracy4', 'adhocracy4'
+        ))
         a4_paths = super().find_files(get_module_dir('adhocracy4'))
         apps_paths = super().find_files(path.relpath(get_module_dir('apps')))
         meinberlin_paths = super().find_files(


Test patch:
Repository: liqd/a4-meinberlin

Issue:
Make creator a readonly field in django admin
With a large user base the dropdown used for the user selection in django admin becomes unresponsive. As there is no apparent reason to change the creator of an object (comment, item, rate, poll, ...) the creator field should be made read_only.

The same problem occurs for every model where a user is set as a foreign key (Action: actor, Projects: member/moderator, Organisation: initiator)

The readonly property can either be set on every Admin class by setting ` readonly_fields = ('creator',)` individually or by using a custom `A4Admin(admin.ModelAdmin)` which has to be set as the parent for every Admin class used.


Discussion:

Resolving patch:
diff --git a/apps/documents/admin.py b/apps/documents/admin.py
--- a/apps/documents/admin.py
+++ b/apps/documents/admin.py
@@ -3,9 +3,12 @@
 from . import models
 
 
+@admin.register(models.Paragraph)
 class ParagraphAdmin(admin.ModelAdmin):
     list_filter = ('chapter',)
+    readonly_fields = ('creator',)
 
 
-admin.site.register(models.Chapter)
-admin.site.register(models.Paragraph, ParagraphAdmin)
+@admin.register(models.Chapter)
+class ChapterAdmin(admin.ModelAdmin):
+    readonly_fields = ('creator', )
diff --git a/apps/offlineevents/admin.py b/apps/offlineevents/admin.py
--- a/apps/offlineevents/admin.py
+++ b/apps/offlineevents/admin.py
@@ -2,4 +2,7 @@
 
 from . import models
 
-admin.site.register(models.OfflineEvent, admin.ModelAdmin)
+
+@admin.register(models.OfflineEvent)
+class OfflineEventAdmin(admin.ModelAdmin):
+    readonly_fields = ('creator', )
diff --git a/apps/polls/admin.py b/apps/polls/admin.py
--- a/apps/polls/admin.py
+++ b/apps/polls/admin.py
@@ -7,15 +7,8 @@ class ChoiceInline(admin.TabularInline):
     model = models.Choice
 
 
+@admin.register(models.Question)
 class QuestionAdmin(admin.ModelAdmin):
     inlines = [
         ChoiceInline
     ]
-
-
-class VoteAdmin(admin.ModelAdmin):
-    list_filter = ('choice__question',)
-
-
-admin.site.register(models.Question, QuestionAdmin)
-admin.site.register(models.Vote, VoteAdmin)


Test patch:
Repository: liqd/a4-meinberlin

Issue:
datetimepicker unique ids
<img width="980" alt="screen shot 2017-06-01 at 15 59 21" src="https://cloud.githubusercontent.com/assets/16354712/26683219/61f8eb4c-46e3-11e7-851b-148bff927bd4.png">



Discussion:

Resolving patch:
diff --git a/apps/contrib/widgets.py b/apps/contrib/widgets.py
--- a/apps/contrib/widgets.py
+++ b/apps/contrib/widgets.py
@@ -71,12 +71,14 @@ def render(self, name, value, attrs=None):
         date_attrs = self.build_attrs(attrs)
         date_attrs.update({
             'class': 'datepicker',
-            'placeholder': formats.localize_input(datetime.date.today())
+            'placeholder': formats.localize_input(datetime.date.today()),
+            'id': name + '_date'
         })
         time_attrs = self.build_attrs(attrs)
         time_attrs.update({
             'class': 'timepicker',
             'placeholder': '00:00',
+            'id': name + '_time'
         })
 
         if isinstance(value, datetime.datetime):


Test patch:
Repository: liqd/a4-meinberlin

Issue:
Use <time> element for dates
This way screen readers (and other ATs) know that it should be read as a date.


Discussion:

Resolving patch:
diff --git a/meinberlin/apps/contrib/templatetags/contrib_tags.py b/meinberlin/apps/contrib/templatetags/contrib_tags.py
--- a/meinberlin/apps/contrib/templatetags/contrib_tags.py
+++ b/meinberlin/apps/contrib/templatetags/contrib_tags.py
@@ -1,5 +1,8 @@
 from django import template
+from django.forms.utils import flatatt
+from django.template import defaultfilters
 from django.template.loader import render_to_string
+from django.utils.safestring import mark_safe
 
 register = template.Library()
 
@@ -48,3 +51,18 @@ def project_tile_image_copyright(project):
         return project.image_copyright
     else:
         return None
+
+
+@register.simple_tag()
+def html_date(value, displayfmt=None, datetimefmt='c', **kwargs):
+    """Format a date and wrap it in a html <time> element.
+
+    Additional html attributes may be provided as kwargs (e.g. 'class').
+    """
+    displaydate = defaultfilters.date(value, displayfmt)
+    datetime = defaultfilters.date(value, datetimefmt)
+    attribs = flatatt(kwargs)
+    result = '<time %s datetime="%s">%s</time>' % (attribs,
+                                                   datetime,
+                                                   displaydate)
+    return mark_safe(result)


Test patch:
Repository: liqd/a4-meinberlin

Issue:
ckeditor accessible name contains technical id



Discussion:
I opened an [issue](https://github.com/django-ckeditor/django-ckeditor/issues/424) on the django_ckeditor repo. While we could probably monkey patch this, I hope this can be done correctly.

Resolving patch:
diff --git a/meinberlin/config/settings/base.py b/meinberlin/config/settings/base.py
--- a/meinberlin/config/settings/base.py
+++ b/meinberlin/config/settings/base.py
@@ -13,6 +13,8 @@
 # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
 import os
 
+from django.utils.translation import ugettext_lazy as _
+
 CONFIG_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
 PROJECT_DIR = os.path.dirname(CONFIG_DIR)
 BASE_DIR = os.path.dirname(PROJECT_DIR)
@@ -269,6 +271,7 @@
 CKEDITOR_CONFIGS = {
     'default': {
         'width': '100%',
+        'title': _('Rich text editor'),
         'toolbar': 'Custom',
         'toolbar_Custom': [
             ['Bold', 'Italic', 'Underline'],
@@ -278,6 +281,7 @@
     },
     'image-editor': {
         'width': '100%',
+        'title': _('Rich text editor'),
         'toolbar': 'Custom',
         'toolbar_Custom': [
             ['Bold', 'Italic', 'Underline'],


Test patch:
Repository: liqd/a4-meinberlin

Issue:
Import of Bezirksregionen stopped working
`$ manage.py import_geodata --gdal-legacy`

Leads to a `KeyError`, probably the data format has changed.



Discussion:

Resolving patch:
diff --git a/meinberlin/apps/maps/management/commands/import_geodata.py b/meinberlin/apps/maps/management/commands/import_geodata.py
--- a/meinberlin/apps/maps/management/commands/import_geodata.py
+++ b/meinberlin/apps/maps/management/commands/import_geodata.py
@@ -40,13 +40,13 @@ def _import_districts(self):
 
     def _import_regions(self):
         url = 'http://fbinter.stadt-berlin.de/fb/' \
-              'wfs/geometry/senstadt/re_bezirksregion'
+              'wfs/geometry/senstadt/re_bezirksregion/'
         tmpfile = '/tmp/bezirksregions.json'
         self._download_geodata(tmpfile, url,
                                'fis:re_bezirksregion')
         data = json.load(open(tmpfile, 'r'))
         for feature in data['features']:
-            district = feature['properties']['BEZIRK']
+            district = feature['properties']['BEZNAME']
             region = feature['properties']['BZR_NAME']
             category = self._preset_category(district)
             if not map_models.MapPreset.objects.filter(name=region).exists():
@@ -78,7 +78,7 @@ def _download_geodata(self, filename: str, url: str, layer: str):
 
         src = 'WFS:{}{}'.format(
             url,
-            '?TYPENAMES=GML2' if self.is_gdal_legacy else ''
+            '?VERSION=1.1.0' if self.is_gdal_legacy else ''
         )
         try:
             print('Trying to download file from {}'.format(url))


Test patch:
Repository: liqd/a4-meinberlin

Issue:
TopicPrio and MapTopicPrio have Categories disabled
Currently Map|TopicPrio are not categorizable [1]. Not sure if this is intended as they had categories before the category refactoring.

Note, that the CategoryFilters are still defined in map|topicprio/views.py and in the templates. If we disable categories on them we should remove them from that files, too.


[1] defined in the settings:
```
A4_CATEGORIZABLE = (
    ('meinberlin_ideas', 'idea'),
    ('meinberlin_mapideas', 'mapidea'),
    ('meinberlin_budgeting', 'proposal'),
    ('meinberlin_kiezkasse', 'proposal'),
)
```



Discussion:
talked to the PM: categories should be enabled for map|topicprio

Resolving patch:
diff --git a/meinberlin/config/settings/base.py b/meinberlin/config/settings/base.py
--- a/meinberlin/config/settings/base.py
+++ b/meinberlin/config/settings/base.py
@@ -331,22 +331,22 @@
     ('meinberlin_ideas', 'idea'),
     ('meinberlin_mapideas', 'mapidea'),
     ('meinberlin_budgeting', 'proposal'),
+    ('meinberlin_kiezkasse', 'proposal'),
     ('meinberlin_topicprio', 'topic'),
     ('meinberlin_maptopicprio', 'maptopic'),
-    ('meinberlin_kiezkasse', 'proposal'),
 )
 
 A4_COMMENTABLES = (
     ('a4comments', 'comment'),
     ('meinberlin_ideas', 'idea'),
-    ('meinberlin_documents', 'chapter'),
-    ('meinberlin_documents', 'paragraph'),
     ('meinberlin_mapideas', 'mapidea'),
     ('meinberlin_budgeting', 'proposal'),
+    ('meinberlin_kiezkasse', 'proposal'),
     ('meinberlin_topicprio', 'topic'),
     ('meinberlin_maptopicprio', 'maptopic'),
     ('meinberlin_polls', 'poll'),
-    ('meinberlin_kiezkasse', 'proposal'),
+    ('meinberlin_documents', 'chapter'),
+    ('meinberlin_documents', 'paragraph'),
 )
 
 A4_REPORTABLES = (
@@ -354,8 +354,6 @@
     ('meinberlin_ideas', 'idea'),
     ('meinberlin_mapideas', 'mapidea'),
     ('meinberlin_budgeting', 'proposal'),
-    ('meinberlin_topicprio', 'topic'),
-    ('meinberlin_maptopicprio', 'maptopic'),
     ('meinberlin_kiezkasse', 'proposal'),
 )
 
@@ -381,6 +379,8 @@
     ('meinberlin_mapideas', 'mapidea'),
     ('meinberlin_budgeting', 'proposal'),
     ('meinberlin_kiezkasse', 'proposal'),
+    ('meinberlin_topicprio', 'topic'),
+    ('meinberlin_maptopicprio', 'maptopic'),
 )
 
 


Test patch:
Repository: liqd/a4-meinberlin

Issue:
Make private project setting more clear
Currently the checkbox is kind of hidden and it is not instantly clear what is meant by "Access to Project". The setting should be more obvious and it's consequences more clear.

Opin has another way of showing the setting which we could/should adopt


Discussion:
Related: https://github.com/liqd/adhocracy4/pull/190
See https://github.com/liqd/a4-opin/pull/874 for the solution used in opin.

Resolving patch:
diff --git a/meinberlin/apps/dashboard2/forms.py b/meinberlin/apps/dashboard2/forms.py
--- a/meinberlin/apps/dashboard2/forms.py
+++ b/meinberlin/apps/dashboard2/forms.py
@@ -1,5 +1,6 @@
 from django import forms
 from django.contrib.auth import get_user_model
+from django.forms import RadioSelect
 from django.forms import inlineformset_factory
 from django.utils.translation import ugettext_lazy as _
 
@@ -53,6 +54,14 @@ class Meta:
                   'tile_image', 'tile_image_copyright',
                   'is_archived', 'is_public']
         required_for_project_publish = ['name', 'description']
+        widgets = {
+            'is_public': RadioSelect(
+                choices=[
+                    (True, _('All users can participate (public).')),
+                    (False, _('Only invited users can participate (private).'))
+                ]
+            ),
+        }
 
     def __init__(self, *args, **kwargs):
         super().__init__(*args, **kwargs)


Test patch:
Repository: liqd/a4-meinberlin

Issue:
Projectcontainer active projects count broken
https://mein.berlin.de/projects/stadtforum-berlin-wohnen/

shows `7 of 4` active projects.


Discussion:
there is probably a problem with private and maybe even unpublished projects :(
(or maybe its because multiple phases are active the same time and we are missing a distinct)

Resolving patch:
diff --git a/meinberlin/apps/projectcontainers/models.py b/meinberlin/apps/projectcontainers/models.py
--- a/meinberlin/apps/projectcontainers/models.py
+++ b/meinberlin/apps/projectcontainers/models.py
@@ -21,7 +21,7 @@ def active_projects(self):
         now = timezone.now()
         return self.projects.filter(
             module__phase__start_date__lte=now,
-            module__phase__end_date__gt=now)
+            module__phase__end_date__gt=now).distinct()
 
     @property
     def phases(self):


Test patch:
Repository: liqd/a4-meinberlin

Issue:
Maptopicprio: Polygon may not be set
When I try to create a maptopic in the dashboard, it is not guaranteed that a polygon is already set. The map for setting a point therefore fails to display.


Discussion:

Resolving patch:
diff --git a/meinberlin/apps/maptopicprio/dashboard.py b/meinberlin/apps/maptopicprio/dashboard.py
--- a/meinberlin/apps/maptopicprio/dashboard.py
+++ b/meinberlin/apps/maptopicprio/dashboard.py
@@ -15,7 +15,12 @@ class MapTopicEditComponent(DashboardComponent):
 
     def is_effective(self, module):
         module_app = module.phases[0].content().app
-        return module_app == 'meinberlin_maptopicprio'
+        if module_app != 'meinberlin_maptopicprio':
+            return False
+        elif module.settings_instance.polygon == '':
+            return False
+        else:
+            return True
 
     def get_progress(self, module):
         if models.MapTopic.objects.filter(module=module).exists():


Test patch:
Repository: liqd/a4-meinberlin

Issue:
On creating a plan, "Berlin" may be selected as district
"Berlin" is part of the dropdown in the vorhaben create and update form

@MagdaN should i add this issue to the whiteboard or taiga, too?


Discussion:

Resolving patch:
diff --git a/meinberlin/apps/plans/models.py b/meinberlin/apps/plans/models.py
--- a/meinberlin/apps/plans/models.py
+++ b/meinberlin/apps/plans/models.py
@@ -2,6 +2,7 @@
 from django.conf import settings
 from django.core.urlresolvers import reverse
 from django.db import models
+from django.db.models import Q
 from django.utils.translation import ugettext_lazy as _
 
 from adhocracy4 import transforms
@@ -42,7 +43,7 @@ class Plan(UserGeneratedContentModel):
     )
     district = models.ForeignKey(
         MapPreset,
-        limit_choices_to={'category__name': 'Berlin'})
+        limit_choices_to=Q(category__name='Berlin') & ~Q(name='Berlin'))
     contact = models.TextField(max_length=255, verbose_name=_('Contact'))
     cost = models.PositiveIntegerField(blank=True, null=True,
                                        verbose_name=_('Cost'))


Test patch:
Repository: liqd/a4-meinberlin

Issue:
Type Error in plans list
if a project connected to the plans list adds a module, but no phase dates, the date might become none and the whole list will not be shown


Discussion:

Resolving patch:
diff --git a/meinberlin/apps/plans/views.py b/meinberlin/apps/plans/views.py
--- a/meinberlin/apps/plans/views.py
+++ b/meinberlin/apps/plans/views.py
@@ -54,7 +54,8 @@ def _get_status_string(self, projects):
             phases = project.phases
             if phases.active_phases():
                 return ugettext('running')
-            if phases.future_phases():
+            if phases.future_phases() and \
+               phases.future_phases().first().start_date:
                 date = phases.future_phases().first().start_date
                 if not future_phase:
                     future_phase = date


Test patch:
Repository: liqd/a4-meinberlin

Issue:
translation in alert: "Du" instead of "Sie"
![screenshot from 2018-04-30 10-19-21](https://user-images.githubusercontent.com/8178179/39419398-10b9656c-4c60-11e8-9a47-23c86f0b2922.png)



Discussion:

Resolving patch:
diff --git a/meinberlin/apps/contrib/django_standard_messages.py b/meinberlin/apps/contrib/django_standard_messages.py
new file mode 100644
--- /dev/null
+++ b/meinberlin/apps/contrib/django_standard_messages.py
@@ -0,0 +1,13 @@
+def _(s):
+    return s
+
+
+django_standard_messages_to_override = [
+    _("You have signed out."),
+    _("Verify Your E-mail Address"),
+    _("You must type the same password each time."),
+    _("You have confirmed %(email)s."),
+    _("You cannot remove your primary e-mail address (%(email)s)."),
+    _("We have sent you an e-mail. Please contact us if "
+      "you do not receive it within a few minutes.")
+]


Test patch:
Repository: liqd/a4-meinberlin

Issue:
Untranslated "Plans"

![screenshot from 2018-05-16 15-53-17](https://user-images.githubusercontent.com/2066372/40121189-48c8fe68-5921-11e8-9756-04eab5a7a83b.png)

Maybe we just remove it since there is already a header


Discussion:
Issues concerning translations and wording [#1368][#1352] [#1353][#610][#1091]

Resolving patch:
diff --git a/meinberlin/apps/plans/forms.py b/meinberlin/apps/plans/forms.py
--- a/meinberlin/apps/plans/forms.py
+++ b/meinberlin/apps/plans/forms.py
@@ -64,7 +64,8 @@ def clean(self, value):
 
 
 class ProjectPlansDashboardForm(ProjectDashboardForm):
-    plans = CustomMultipleChoiceField(queryset=None)
+    plans = CustomMultipleChoiceField(queryset=None,
+                                      label=_('Plans'))
 
     class Meta:
         model = project_models.Project


Test patch:
Repository: liqd/a4-meinberlin

Issue:
make it possible to detach plan from project
now that a connection of a project with a plan is no longer obligatory, initiators need to be able to undo a connection. Once I click on a plan I can no longer NOT connect to a plan, which should be possible.

![image](https://user-images.githubusercontent.com/35491681/47439596-f041f000-d7ac-11e8-85f8-13407e97ada3.png)



Discussion:
@MagdaN I can't find a solution. One of these days. 
Well, the projects are stored in a manytomanyfield for the plans, and the widget is replaced by the radiioselect. Even though the field is not required, in the django-admin there is also no option to delete all projects from the plans. One is always left.
How do we add some kind of null-option or clear the saved project?

Resolving patch:
diff --git a/meinberlin/apps/plans/forms.py b/meinberlin/apps/plans/forms.py
--- a/meinberlin/apps/plans/forms.py
+++ b/meinberlin/apps/plans/forms.py
@@ -55,10 +55,10 @@ def __init__(self, *args, **kwargs):
 
 class CustomMultipleChoiceField(forms.ModelMultipleChoiceField):
 
-    widget = forms.RadioSelect
+    widget = forms.Select
 
     def clean(self, value):
-        if value is None:
+        if not value:
             return super().clean([])
         return super().clean([value])
 
@@ -79,5 +79,7 @@ def save(self, commit=False):
     def __init__(self, *args, **kwargs):
         super().__init__(*args, **kwargs)
         self.initial['plans'] = self.instance.plans.all()
-        self.fields['plans'
-                    ].queryset = self.instance.organisation.plan_set.all()
+        self.fields['plans'].required = False
+        self.fields['plans'].empty_label = '----------'
+        self.fields['plans'].queryset = \
+            self.instance.organisation.plan_set.all()


Test patch:
Repository: liqd/a4-meinberlin

Issue:
add "gesamtstädtisch" as null-option to projects



Discussion:
@MagdaN And another one, I couldn't solve. It should be possible also in the model-admin: https://docs.djangoproject.com/en/1.11/ref/contrib/admin/#django.contrib.admin.ModelAdmin.empty_value_display
But I couldn't get it working. Didn't want to change it in A4.
It shouldn't matter for now though. As long as the field can only be filled in the model admin, we can either tell the admins or put it in the helptext, that no district ('-------') needs to be chosen for city wide. 

Resolving patch:
diff --git a/meinberlin/apps/projects/admin.py b/meinberlin/apps/projects/admin.py
--- a/meinberlin/apps/projects/admin.py
+++ b/meinberlin/apps/projects/admin.py
@@ -40,6 +40,11 @@ class ProjectAdmin(admin.ModelAdmin):
         }),
     )
 
+    def formfield_for_foreignkey(self, db_field, request, **kwargs):
+        if db_field.name == 'administrative_district':
+            kwargs['empty_label'] = _('City wide')
+        return super().formfield_for_foreignkey(db_field, request, **kwargs)
+
 
 # Overwrite adhocracy4.projects.admin
 admin.site.unregister(models.Project)


Test patch: