We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Fix "similar-code" issue in website/activemembers/admin.py
Similar blocks of code found in 2 locations. Consider refactoring.

https://codeclimate.com/github/svthalia/concrexit/website/activemembers/admin.py#issue_5eceacbde96d31000100042c

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `website/activemembers/admin.py`
Content:
```
1 """Registers admin interfaces for the activemembers module"""
2 import csv
3 import datetime
4 
5 from django import forms
6 from django.contrib import admin, messages
7 from django.db.models import Q
8 from django.http import HttpResponse
9 from django.utils import timezone
10 from django.utils.translation import gettext_lazy as _
11 
12 from activemembers import models
13 from activemembers.forms import MemberGroupMembershipForm, MemberGroupForm
14 from utils.snippets import datetime_to_lectureyear
15 from utils.translation import TranslatedModelAdmin
16 
17 
18 class MemberGroupMembershipInlineFormSet(forms.BaseInlineFormSet):
19     """
20     Solely here for performance reasons.
21 
22     Needed because the `__str__()` of `MemberGroupMembership` (which is
23     displayed above each inline form) uses the username, name of the member
24     and name of the group.
25     """
26 
27     def __init__(self, *args, **kwargs):
28         super().__init__(*args, **kwargs)
29         self.queryset = self.queryset.select_related("member", "group").filter(
30             until=None
31         )
32 
33 
34 class MemberGroupMembershipInline(admin.StackedInline):
35     """Inline for group memberships"""
36 
37     model = models.MemberGroupMembership
38     formset = MemberGroupMembershipInlineFormSet
39     can_delete = False
40     ordering = ("since",)
41     extra = 0
42     autocomplete_fields = ("member",)
43 
44 
45 @admin.register(models.Committee)
46 class CommitteeAdmin(TranslatedModelAdmin):
47     """Manage the committees"""
48 
49     inlines = (MemberGroupMembershipInline,)
50     form = MemberGroupForm
51     list_display = ("name", "since", "until", "active", "email")
52     list_filter = (
53         "until",
54         "active",
55     )
56     search_fields = ("name", "description")
57     filter_horizontal = ("permissions",)
58 
59     fields = (
60         "name",
61         "description",
62         "photo",
63         "permissions",
64         "since",
65         "until",
66         "contact_mailinglist",
67         "contact_email",
68         "active",
69         "display_members",
70     )
71 
72     def email(self, instance):
73         if instance.contact_email:
74             return instance.contact_email
75         elif instance.contact_mailinglist:
76             return instance.contact_mailinglist.name + "@thalia.nu"
77         return None
78 
79 
80 @admin.register(models.Society)
81 class SocietyAdmin(TranslatedModelAdmin):
82     """Manage the societies"""
83 
84     inlines = (MemberGroupMembershipInline,)
85     form = MemberGroupForm
86     list_display = ("name", "since", "until", "active", "email")
87     list_filter = (
88         "until",
89         "active",
90     )
91     search_fields = ("name", "description")
92     filter_horizontal = ("permissions",)
93 
94     fields = (
95         "name",
96         "description",
97         "photo",
98         "permissions",
99         "since",
100         "until",
101         "contact_mailinglist",
102         "contact_email",
103         "active",
104         "display_members",
105     )
106 
107     def email(self, instance):
108         if instance.contact_email:
109             return instance.contact_email
110         elif instance.contact_mailinglist:
111             return instance.contact_mailinglist.name + "@thalia.nu"
112         return None
113 
114 
115 @admin.register(models.Board)
116 class BoardAdmin(TranslatedModelAdmin):
117     """Manage the board"""
118 
119     inlines = (MemberGroupMembershipInline,)
120     form = MemberGroupForm
121     exclude = ("is_board",)
122     filter_horizontal = ("permissions",)
123 
124     fields = (
125         "name",
126         "description",
127         "photo",
128         "permissions",
129         "contact_mailinglist",
130         "contact_email",
131         "since",
132         "until",
133         "display_members",
134     )
135 
136 
137 class TypeFilter(admin.SimpleListFilter):
138     """Filter memberships on board-only"""
139 
140     title = _("group memberships")
141     parameter_name = "group_type"
142 
143     def lookups(self, request, model_admin):
144         return [
145             ("boards", _("Only boards")),
146             ("committees", _("Only committees")),
147             ("societies", _("Only societies")),
148         ]
149 
150     def queryset(self, request, queryset):
151         if self.value() == "boards":
152             return queryset.exclude(group__board=None)
153         elif self.value() == "committees":
154             return queryset.exclude(group__committee=None)
155         elif self.value() == "societies":
156             return queryset.exclude(group__society=None)
157 
158         return queryset
159 
160 
161 class LectureYearFilter(admin.SimpleListFilter):
162     """Filter the memberships on those started or ended in a lecture year"""
163 
164     title = _("lecture year")
165     parameter_name = "lecture_year"
166 
167     def lookups(self, request, model_admin):
168         current_year = datetime_to_lectureyear(timezone.now())
169         first_year = datetime_to_lectureyear(
170             models.MemberGroupMembership.objects.earliest("since").since
171         )
172 
173         return [
174             (year, "{}-{}".format(year, year + 1))
175             for year in range(first_year, current_year + 1)
176         ]
177 
178     def queryset(self, request, queryset):
179         if not self.value():
180             return queryset
181 
182         year = int(self.value())
183         first_of_september = datetime.date(year=year, month=9, day=1)
184 
185         return queryset.exclude(until__lt=first_of_september)
186 
187 
188 class ActiveMembershipsFilter(admin.SimpleListFilter):
189     """Filter the memberships by whether they are active or not"""
190 
191     title = _("active memberships")
192     parameter_name = "active"
193 
194     def lookups(self, request, model_name):
195         return (
196             ("active", _("Active")),
197             ("inactive", _("Inactive")),
198         )
199 
200     def queryset(self, request, queryset):
201         now = timezone.now()
202 
203         if self.value() == "active":
204             return queryset.filter(Q(until__isnull=True) | Q(until__gte=now))
205 
206         if self.value() == "inactive":
207             return queryset.filter(until__lt=now)
208 
209 
210 @admin.register(models.MemberGroupMembership)
211 class MemberGroupMembershipAdmin(TranslatedModelAdmin):
212     """Manage the group memberships"""
213 
214     form = MemberGroupMembershipForm
215     list_display = ("member", "group", "since", "until", "chair", "role")
216     list_filter = ("group", TypeFilter, LectureYearFilter, ActiveMembershipsFilter)
217     list_select_related = (
218         "member",
219         "group",
220     )
221     search_fields = ("member__first_name", "member__last_name", "member__email")
222     date_hierarchy = "since"
223     actions = ("export",)
224 
225     def changelist_view(self, request, extra_context=None):
226         self.message_user(
227             request,
228             _(
229                 "Do not edit existing memberships if the "
230                 "chair of a group has changed, add a "
231                 "new membership instead."
232             ),
233             messages.WARNING,
234         )
235         return super().changelist_view(request, extra_context)
236 
237     def export(self, request, queryset):
238         response = HttpResponse(content_type="text/csv")
239         response["Content-Disposition"] = (
240             "attachment;" "filename=" '"group_memberships.csv"'
241         )
242         writer = csv.writer(response)
243         writer.writerow(
244             [
245                 _("First name"),
246                 _("Last name"),
247                 _("Email"),
248                 _("Group"),
249                 _("Member since"),
250                 _("Member until"),
251                 _("Chair of the group"),
252                 _("Role"),
253             ]
254         )
255 
256         for membership in queryset:
257             writer.writerow(
258                 [
259                     membership.member.first_name,
260                     membership.member.last_name,
261                     membership.member.email,
262                     membership.group,
263                     membership.since,
264                     membership.until,
265                     membership.chair,
266                     membership.role,
267                 ]
268             )
269 
270         return response
271 
272     export.short_description = _("Export selected memberships")
273 
274 
275 @admin.register(models.Mentorship)
276 class MentorshipAdmin(admin.ModelAdmin):
277     """Manage the mentorships"""
278 
279     autocomplete_fields = ("member",)
280     search_fields = ("member__first_name", "member__last_name")
281     list_filter = ("year",)
282
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/website/activemembers/admin.py b/website/activemembers/admin.py
--- a/website/activemembers/admin.py
+++ b/website/activemembers/admin.py
@@ -42,9 +42,8 @@
     autocomplete_fields = ("member",)
 
 
-@admin.register(models.Committee)
-class CommitteeAdmin(TranslatedModelAdmin):
-    """Manage the committees"""
+class MemberGroupAdmin(TranslatedModelAdmin):
+    """Manage the member groups"""
 
     inlines = (MemberGroupMembershipInline,)
     form = MemberGroupForm
@@ -77,39 +76,18 @@
         return None
 
 
-@admin.register(models.Society)
-class SocietyAdmin(TranslatedModelAdmin):
-    """Manage the societies"""
+@admin.register(models.Committee)
+class CommitteeAdmin(MemberGroupAdmin):
+    """Manage the committees"""
 
-    inlines = (MemberGroupMembershipInline,)
-    form = MemberGroupForm
-    list_display = ("name", "since", "until", "active", "email")
-    list_filter = (
-        "until",
-        "active",
-    )
-    search_fields = ("name", "description")
-    filter_horizontal = ("permissions",)
+    pass
 
-    fields = (
-        "name",
-        "description",
-        "photo",
-        "permissions",
-        "since",
-        "until",
-        "contact_mailinglist",
-        "contact_email",
-        "active",
-        "display_members",
-    )
 
-    def email(self, instance):
-        if instance.contact_email:
-            return instance.contact_email
-        elif instance.contact_mailinglist:
-            return instance.contact_mailinglist.name + "@thalia.nu"
-        return None
+@admin.register(models.Society)
+class SocietyAdmin(MemberGroupAdmin):
+    """Manage the societies"""
+
+    pass
 
 
 @admin.register(models.Board)

{"golden_diff": "diff --git a/website/activemembers/admin.py b/website/activemembers/admin.py\n--- a/website/activemembers/admin.py\n+++ b/website/activemembers/admin.py\n@@ -42,9 +42,8 @@\n     autocomplete_fields = (\"member\",)\n \n \n-@admin.register(models.Committee)\n-class CommitteeAdmin(TranslatedModelAdmin):\n-    \"\"\"Manage the committees\"\"\"\n+class MemberGroupAdmin(TranslatedModelAdmin):\n+    \"\"\"Manage the member groups\"\"\"\n \n     inlines = (MemberGroupMembershipInline,)\n     form = MemberGroupForm\n@@ -77,39 +76,18 @@\n         return None\n \n \n-@admin.register(models.Society)\n-class SocietyAdmin(TranslatedModelAdmin):\n-    \"\"\"Manage the societies\"\"\"\n+@admin.register(models.Committee)\n+class CommitteeAdmin(MemberGroupAdmin):\n+    \"\"\"Manage the committees\"\"\"\n \n-    inlines = (MemberGroupMembershipInline,)\n-    form = MemberGroupForm\n-    list_display = (\"name\", \"since\", \"until\", \"active\", \"email\")\n-    list_filter = (\n-        \"until\",\n-        \"active\",\n-    )\n-    search_fields = (\"name\", \"description\")\n-    filter_horizontal = (\"permissions\",)\n+    pass\n \n-    fields = (\n-        \"name\",\n-        \"description\",\n-        \"photo\",\n-        \"permissions\",\n-        \"since\",\n-        \"until\",\n-        \"contact_mailinglist\",\n-        \"contact_email\",\n-        \"active\",\n-        \"display_members\",\n-    )\n \n-    def email(self, instance):\n-        if instance.contact_email:\n-            return instance.contact_email\n-        elif instance.contact_mailinglist:\n-            return instance.contact_mailinglist.name + \"@thalia.nu\"\n-        return None\n+@admin.register(models.Society)\n+class SocietyAdmin(MemberGroupAdmin):\n+    \"\"\"Manage the societies\"\"\"\n+\n+    pass\n \n \n @admin.register(models.Board)\n", "issue": "Fix \"similar-code\" issue in website/activemembers/admin.py\nSimilar blocks of code found in 2 locations. Consider refactoring.\n\nhttps://codeclimate.com/github/svthalia/concrexit/website/activemembers/admin.py#issue_5eceacbde96d31000100042c\n", "before_files": [{"content": "\"\"\"Registers admin interfaces for the activemembers module\"\"\"\nimport csv\nimport datetime\n\nfrom django import forms\nfrom django.contrib import admin, messages\nfrom django.db.models import Q\nfrom django.http import HttpResponse\nfrom django.utils import timezone\nfrom django.utils.translation import gettext_lazy as _\n\nfrom activemembers import models\nfrom activemembers.forms import MemberGroupMembershipForm, MemberGroupForm\nfrom utils.snippets import datetime_to_lectureyear\nfrom utils.translation import TranslatedModelAdmin\n\n\nclass MemberGroupMembershipInlineFormSet(forms.BaseInlineFormSet):\n    \"\"\"\n    Solely here for performance reasons.\n\n    Needed because the `__str__()` of `MemberGroupMembership` (which is\n    displayed above each inline form) uses the username, name of the member\n    and name of the group.\n    \"\"\"\n\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.queryset = self.queryset.select_related(\"member\", \"group\").filter(\n            until=None\n        )\n\n\nclass MemberGroupMembershipInline(admin.StackedInline):\n    \"\"\"Inline for group memberships\"\"\"\n\n    model = models.MemberGroupMembership\n    formset = MemberGroupMembershipInlineFormSet\n    can_delete = False\n    ordering = (\"since\",)\n    extra = 0\n    autocomplete_fields = (\"member\",)\n\n\n@admin.register(models.Committee)\nclass CommitteeAdmin(TranslatedModelAdmin):\n    \"\"\"Manage the committees\"\"\"\n\n    inlines = (MemberGroupMembershipInline,)\n    form = MemberGroupForm\n    list_display = (\"name\", \"since\", \"until\", \"active\", \"email\")\n    list_filter = (\n        \"until\",\n        \"active\",\n    )\n    search_fields = (\"name\", \"description\")\n    filter_horizontal = (\"permissions\",)\n\n    fields = (\n        \"name\",\n        \"description\",\n        \"photo\",\n        \"permissions\",\n        \"since\",\n        \"until\",\n        \"contact_mailinglist\",\n        \"contact_email\",\n        \"active\",\n        \"display_members\",\n    )\n\n    def email(self, instance):\n        if instance.contact_email:\n            return instance.contact_email\n        elif instance.contact_mailinglist:\n            return instance.contact_mailinglist.name + \"@thalia.nu\"\n        return None\n\n\n@admin.register(models.Society)\nclass SocietyAdmin(TranslatedModelAdmin):\n    \"\"\"Manage the societies\"\"\"\n\n    inlines = (MemberGroupMembershipInline,)\n    form = MemberGroupForm\n    list_display = (\"name\", \"since\", \"until\", \"active\", \"email\")\n    list_filter = (\n        \"until\",\n        \"active\",\n    )\n    search_fields = (\"name\", \"description\")\n    filter_horizontal = (\"permissions\",)\n\n    fields = (\n        \"name\",\n        \"description\",\n        \"photo\",\n        \"permissions\",\n        \"since\",\n        \"until\",\n        \"contact_mailinglist\",\n        \"contact_email\",\n        \"active\",\n        \"display_members\",\n    )\n\n    def email(self, instance):\n        if instance.contact_email:\n            return instance.contact_email\n        elif instance.contact_mailinglist:\n            return instance.contact_mailinglist.name + \"@thalia.nu\"\n        return None\n\n\n@admin.register(models.Board)\nclass BoardAdmin(TranslatedModelAdmin):\n    \"\"\"Manage the board\"\"\"\n\n    inlines = (MemberGroupMembershipInline,)\n    form = MemberGroupForm\n    exclude = (\"is_board\",)\n    filter_horizontal = (\"permissions\",)\n\n    fields = (\n        \"name\",\n        \"description\",\n        \"photo\",\n        \"permissions\",\n        \"contact_mailinglist\",\n        \"contact_email\",\n        \"since\",\n        \"until\",\n        \"display_members\",\n    )\n\n\nclass TypeFilter(admin.SimpleListFilter):\n    \"\"\"Filter memberships on board-only\"\"\"\n\n    title = _(\"group memberships\")\n    parameter_name = \"group_type\"\n\n    def lookups(self, request, model_admin):\n        return [\n            (\"boards\", _(\"Only boards\")),\n            (\"committees\", _(\"Only committees\")),\n            (\"societies\", _(\"Only societies\")),\n        ]\n\n    def queryset(self, request, queryset):\n        if self.value() == \"boards\":\n            return queryset.exclude(group__board=None)\n        elif self.value() == \"committees\":\n            return queryset.exclude(group__committee=None)\n        elif self.value() == \"societies\":\n            return queryset.exclude(group__society=None)\n\n        return queryset\n\n\nclass LectureYearFilter(admin.SimpleListFilter):\n    \"\"\"Filter the memberships on those started or ended in a lecture year\"\"\"\n\n    title = _(\"lecture year\")\n    parameter_name = \"lecture_year\"\n\n    def lookups(self, request, model_admin):\n        current_year = datetime_to_lectureyear(timezone.now())\n        first_year = datetime_to_lectureyear(\n            models.MemberGroupMembership.objects.earliest(\"since\").since\n        )\n\n        return [\n            (year, \"{}-{}\".format(year, year + 1))\n            for year in range(first_year, current_year + 1)\n        ]\n\n    def queryset(self, request, queryset):\n        if not self.value():\n            return queryset\n\n        year = int(self.value())\n        first_of_september = datetime.date(year=year, month=9, day=1)\n\n        return queryset.exclude(until__lt=first_of_september)\n\n\nclass ActiveMembershipsFilter(admin.SimpleListFilter):\n    \"\"\"Filter the memberships by whether they are active or not\"\"\"\n\n    title = _(\"active memberships\")\n    parameter_name = \"active\"\n\n    def lookups(self, request, model_name):\n        return (\n            (\"active\", _(\"Active\")),\n            (\"inactive\", _(\"Inactive\")),\n        )\n\n    def queryset(self, request, queryset):\n        now = timezone.now()\n\n        if self.value() == \"active\":\n            return queryset.filter(Q(until__isnull=True) | Q(until__gte=now))\n\n        if self.value() == \"inactive\":\n            return queryset.filter(until__lt=now)\n\n\n@admin.register(models.MemberGroupMembership)\nclass MemberGroupMembershipAdmin(TranslatedModelAdmin):\n    \"\"\"Manage the group memberships\"\"\"\n\n    form = MemberGroupMembershipForm\n    list_display = (\"member\", \"group\", \"since\", \"until\", \"chair\", \"role\")\n    list_filter = (\"group\", TypeFilter, LectureYearFilter, ActiveMembershipsFilter)\n    list_select_related = (\n        \"member\",\n        \"group\",\n    )\n    search_fields = (\"member__first_name\", \"member__last_name\", \"member__email\")\n    date_hierarchy = \"since\"\n    actions = (\"export\",)\n\n    def changelist_view(self, request, extra_context=None):\n        self.message_user(\n            request,\n            _(\n                \"Do not edit existing memberships if the \"\n                \"chair of a group has changed, add a \"\n                \"new membership instead.\"\n            ),\n            messages.WARNING,\n        )\n        return super().changelist_view(request, extra_context)\n\n    def export(self, request, queryset):\n        response = HttpResponse(content_type=\"text/csv\")\n        response[\"Content-Disposition\"] = (\n            \"attachment;\" \"filename=\" '\"group_memberships.csv\"'\n        )\n        writer = csv.writer(response)\n        writer.writerow(\n            [\n                _(\"First name\"),\n                _(\"Last name\"),\n                _(\"Email\"),\n                _(\"Group\"),\n                _(\"Member since\"),\n                _(\"Member until\"),\n                _(\"Chair of the group\"),\n                _(\"Role\"),\n            ]\n        )\n\n        for membership in queryset:\n            writer.writerow(\n                [\n                    membership.member.first_name,\n                    membership.member.last_name,\n                    membership.member.email,\n                    membership.group,\n                    membership.since,\n                    membership.until,\n                    membership.chair,\n                    membership.role,\n                ]\n            )\n\n        return response\n\n    export.short_description = _(\"Export selected memberships\")\n\n\n@admin.register(models.Mentorship)\nclass MentorshipAdmin(admin.ModelAdmin):\n    \"\"\"Manage the mentorships\"\"\"\n\n    autocomplete_fields = (\"member\",)\n    search_fields = (\"member__first_name\", \"member__last_name\")\n    list_filter = (\"year\",)\n", "path": "website/activemembers/admin.py"}], "after_files": [{"content": "\"\"\"Registers admin interfaces for the activemembers module\"\"\"\nimport csv\nimport datetime\n\nfrom django import forms\nfrom django.contrib import admin, messages\nfrom django.db.models import Q\nfrom django.http import HttpResponse\nfrom django.utils import timezone\nfrom django.utils.translation import gettext_lazy as _\n\nfrom activemembers import models\nfrom activemembers.forms import MemberGroupMembershipForm, MemberGroupForm\nfrom utils.snippets import datetime_to_lectureyear\nfrom utils.translation import TranslatedModelAdmin\n\n\nclass MemberGroupMembershipInlineFormSet(forms.BaseInlineFormSet):\n    \"\"\"\n    Solely here for performance reasons.\n\n    Needed because the `__str__()` of `MemberGroupMembership` (which is\n    displayed above each inline form) uses the username, name of the member\n    and name of the group.\n    \"\"\"\n\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.queryset = self.queryset.select_related(\"member\", \"group\").filter(\n            until=None\n        )\n\n\nclass MemberGroupMembershipInline(admin.StackedInline):\n    \"\"\"Inline for group memberships\"\"\"\n\n    model = models.MemberGroupMembership\n    formset = MemberGroupMembershipInlineFormSet\n    can_delete = False\n    ordering = (\"since\",)\n    extra = 0\n    autocomplete_fields = (\"member\",)\n\n\nclass MemberGroupAdmin(TranslatedModelAdmin):\n    \"\"\"Manage the member groups\"\"\"\n\n    inlines = (MemberGroupMembershipInline,)\n    form = MemberGroupForm\n    list_display = (\"name\", \"since\", \"until\", \"active\", \"email\")\n    list_filter = (\n        \"until\",\n        \"active\",\n    )\n    search_fields = (\"name\", \"description\")\n    filter_horizontal = (\"permissions\",)\n\n    fields = (\n        \"name\",\n        \"description\",\n        \"photo\",\n        \"permissions\",\n        \"since\",\n        \"until\",\n        \"contact_mailinglist\",\n        \"contact_email\",\n        \"active\",\n        \"display_members\",\n    )\n\n    def email(self, instance):\n        if instance.contact_email:\n            return instance.contact_email\n        elif instance.contact_mailinglist:\n            return instance.contact_mailinglist.name + \"@thalia.nu\"\n        return None\n\n\n@admin.register(models.Committee)\nclass CommitteeAdmin(MemberGroupAdmin):\n    \"\"\"Manage the committees\"\"\"\n\n    pass\n\n\n@admin.register(models.Society)\nclass SocietyAdmin(MemberGroupAdmin):\n    \"\"\"Manage the societies\"\"\"\n\n    pass\n\n\n@admin.register(models.Board)\nclass BoardAdmin(TranslatedModelAdmin):\n    \"\"\"Manage the board\"\"\"\n\n    inlines = (MemberGroupMembershipInline,)\n    form = MemberGroupForm\n    exclude = (\"is_board\",)\n    filter_horizontal = (\"permissions\",)\n\n    fields = (\n        \"name\",\n        \"description\",\n        \"photo\",\n        \"permissions\",\n        \"contact_mailinglist\",\n        \"contact_email\",\n        \"since\",\n        \"until\",\n        \"display_members\",\n    )\n\n\nclass TypeFilter(admin.SimpleListFilter):\n    \"\"\"Filter memberships on board-only\"\"\"\n\n    title = _(\"group memberships\")\n    parameter_name = \"group_type\"\n\n    def lookups(self, request, model_admin):\n        return [\n            (\"boards\", _(\"Only boards\")),\n            (\"committees\", _(\"Only committees\")),\n            (\"societies\", _(\"Only societies\")),\n        ]\n\n    def queryset(self, request, queryset):\n        if self.value() == \"boards\":\n            return queryset.exclude(group__board=None)\n        elif self.value() == \"committees\":\n            return queryset.exclude(group__committee=None)\n        elif self.value() == \"societies\":\n            return queryset.exclude(group__society=None)\n\n        return queryset\n\n\nclass LectureYearFilter(admin.SimpleListFilter):\n    \"\"\"Filter the memberships on those started or ended in a lecture year\"\"\"\n\n    title = _(\"lecture year\")\n    parameter_name = \"lecture_year\"\n\n    def lookups(self, request, model_admin):\n        current_year = datetime_to_lectureyear(timezone.now())\n        first_year = datetime_to_lectureyear(\n            models.MemberGroupMembership.objects.earliest(\"since\").since\n        )\n\n        return [\n            (year, \"{}-{}\".format(year, year + 1))\n            for year in range(first_year, current_year + 1)\n        ]\n\n    def queryset(self, request, queryset):\n        if not self.value():\n            return queryset\n\n        year = int(self.value())\n        first_of_september = datetime.date(year=year, month=9, day=1)\n\n        return queryset.exclude(until__lt=first_of_september)\n\n\nclass ActiveMembershipsFilter(admin.SimpleListFilter):\n    \"\"\"Filter the memberships by whether they are active or not\"\"\"\n\n    title = _(\"active memberships\")\n    parameter_name = \"active\"\n\n    def lookups(self, request, model_name):\n        return (\n            (\"active\", _(\"Active\")),\n            (\"inactive\", _(\"Inactive\")),\n        )\n\n    def queryset(self, request, queryset):\n        now = timezone.now()\n\n        if self.value() == \"active\":\n            return queryset.filter(Q(until__isnull=True) | Q(until__gte=now))\n\n        if self.value() == \"inactive\":\n            return queryset.filter(until__lt=now)\n\n\n@admin.register(models.MemberGroupMembership)\nclass MemberGroupMembershipAdmin(TranslatedModelAdmin):\n    \"\"\"Manage the group memberships\"\"\"\n\n    form = MemberGroupMembershipForm\n    list_display = (\"member\", \"group\", \"since\", \"until\", \"chair\", \"role\")\n    list_filter = (\"group\", TypeFilter, LectureYearFilter, ActiveMembershipsFilter)\n    list_select_related = (\n        \"member\",\n        \"group\",\n    )\n    search_fields = (\"member__first_name\", \"member__last_name\", \"member__email\")\n    date_hierarchy = \"since\"\n    actions = (\"export\",)\n\n    def changelist_view(self, request, extra_context=None):\n        self.message_user(\n            request,\n            _(\n                \"Do not edit existing memberships if the \"\n                \"chair of a group has changed, add a \"\n                \"new membership instead.\"\n            ),\n            messages.WARNING,\n        )\n        return super().changelist_view(request, extra_context)\n\n    def export(self, request, queryset):\n        response = HttpResponse(content_type=\"text/csv\")\n        response[\"Content-Disposition\"] = (\n            \"attachment;\" \"filename=\" '\"group_memberships.csv\"'\n        )\n        writer = csv.writer(response)\n        writer.writerow(\n            [\n                _(\"First name\"),\n                _(\"Last name\"),\n                _(\"Email\"),\n                _(\"Group\"),\n                _(\"Member since\"),\n                _(\"Member until\"),\n                _(\"Chair of the group\"),\n                _(\"Role\"),\n            ]\n        )\n\n        for membership in queryset:\n            writer.writerow(\n                [\n                    membership.member.first_name,\n                    membership.member.last_name,\n                    membership.member.email,\n                    membership.group,\n                    membership.since,\n                    membership.until,\n                    membership.chair,\n                    membership.role,\n                ]\n            )\n\n        return response\n\n    export.short_description = _(\"Export selected memberships\")\n\n\n@admin.register(models.Mentorship)\nclass MentorshipAdmin(admin.ModelAdmin):\n    \"\"\"Manage the mentorships\"\"\"\n\n    autocomplete_fields = (\"member\",)\n    search_fields = (\"member__first_name\", \"member__last_name\")\n    list_filter = (\"year\",)\n", "path": "website/activemembers/admin.py"}]}