We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
TypeError: '<' not supported between instances of 'datetime.date' and 'NoneType'
Sentry Issue: [CONCREXIT-3T](https://sentry.io/organizations/thalia/issues/1937285207/?referrer=github_integration)

```
TypeError: '<' not supported between instances of 'datetime.date' and 'NoneType'
(5 additional frame(s) were not displayed)
...
  File "django/views/generic/base.py", line 98, in dispatch
    return handler(request, *args, **kwargs)
  File "django/views/generic/detail.py", line 107, in get
    context = self.get_context_data(object=self.object)
  File "members/views.py", line 181, in get_context_data
    societies = member_societies(member)
  File "members/services.py", line 91, in member_societies
    societies = _member_group_memberships(
  File "members/services.py", line 45, in _member_group_memberships
    if data[name]["latest"] < membership.until:
```

--- 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/members/services.py`
Content:
```
1 """Services defined in the members package"""
2 from datetime import date
3 from typing import Callable, List, Dict, Any
4 
5 from django.conf import settings
6 from django.db.models import Q, Count
7 from django.utils import timezone
8 from django.utils.translation import gettext
9 
10 from members import emails
11 from members.models import Membership, Member
12 from utils.snippets import datetime_to_lectureyear
13 
14 
15 def _member_group_memberships(
16     member: Member, condition: Callable[[Membership], bool]
17 ) -> Dict[str, Any]:
18     """
19     Determines the group membership of a user based on a condition
20     :return: Object with group memberships
21     """
22     memberships = member.membergroupmembership_set.all()
23     data = {}
24 
25     for membership in memberships:
26         if not condition(membership):
27             continue
28         period = {
29             "since": membership.since,
30             "until": membership.until,
31             "chair": membership.chair,
32         }
33 
34         if hasattr(membership.group, "board"):
35             period["role"] = membership.role
36 
37         if membership.until is None and hasattr(membership.group, "board"):
38             period["until"] = membership.group.board.until
39 
40         name = membership.group.name
41         if data.get(name):
42             data[name]["periods"].append(period)
43             if data[name]["earliest"] > membership.since:
44                 data[name]["earliest"] = membership.since
45             if data[name]["latest"] < membership.until:
46                 data[name]["latest"] = membership.until
47             data[name]["periods"].sort(key=lambda x: x["since"])
48         else:
49             data[name] = {
50                 "pk": membership.group.pk,
51                 "active": membership.group.active,
52                 "name": name,
53                 "periods": [period],
54                 "url": settings.BASE_URL + membership.group.get_absolute_url(),
55                 "earliest": membership.since,
56                 "latest": membership.until,
57             }
58     return data
59 
60 
61 def member_achievements(member) -> List:
62     """
63     Derives a list of achievements of a member
64     Committee and board memberships + mentorships
65     """
66     achievements = _member_group_memberships(
67         member,
68         lambda membership: (
69             hasattr(membership.group, "board") or hasattr(membership.group, "committee")
70         ),
71     )
72 
73     mentor_years = member.mentorship_set.all()
74     for mentor_year in mentor_years:
75         name = "Mentor in {}".format(mentor_year.year)
76         # Ensure mentorships appear last but are sorted
77         earliest = date.today()
78         earliest = earliest.replace(year=earliest.year + mentor_year.year)
79         if not achievements.get(name):
80             achievements[name] = {
81                 "name": name,
82                 "earliest": earliest,
83             }
84     return sorted(achievements.values(), key=lambda x: x["earliest"])
85 
86 
87 def member_societies(member) -> List:
88     """
89     Derives a list of societies a member was part of
90     """
91     societies = _member_group_memberships(
92         member, lambda membership: (hasattr(membership.group, "society"))
93     )
94     return sorted(societies.values(), key=lambda x: x["earliest"])
95 
96 
97 def gen_stats_member_type() -> Dict[str, int]:
98     """
99     Generate a dictionary where every key is a member type with
100     the value being the number of current members of that type
101     """
102 
103     data = {}
104     for key, display in Membership.MEMBERSHIP_TYPES:
105         data[str(display)] = (
106             Membership.objects.filter(since__lte=date.today())
107             .filter(Q(until__isnull=True) | Q(until__gt=date.today()))
108             .filter(type=key)
109             .count()
110         )
111     return data
112 
113 
114 def gen_stats_year() -> Dict[str, Dict[str, int]]:
115     """
116     Generate list with 6 entries, where each entry represents the total amount
117     of Thalia members in a year. The sixth element contains all the multi-year
118     students.
119     """
120     stats_year = {}
121     current_year = datetime_to_lectureyear(date.today())
122 
123     for i in range(5):
124         new = {}
125         for key, _ in Membership.MEMBERSHIP_TYPES:
126             new[key] = (
127                 Membership.objects.filter(user__profile__starting_year=current_year - i)
128                 .filter(since__lte=date.today())
129                 .filter(Q(until__isnull=True) | Q(until__gt=date.today()))
130                 .filter(type=key)
131                 .count()
132             )
133         stats_year[str(current_year - i)] = new
134 
135     # Add multi year members
136     new = {}
137     for key, _ in Membership.MEMBERSHIP_TYPES:
138         new[key] = (
139             Membership.objects.filter(user__profile__starting_year__lt=current_year - 4)
140             .filter(since__lte=date.today())
141             .filter(Q(until__isnull=True) | Q(until__gt=date.today()))
142             .filter(type=key)
143             .count()
144         )
145     stats_year[str(gettext("Older"))] = new
146 
147     return stats_year
148 
149 
150 def verify_email_change(change_request) -> None:
151     """
152     Mark the email change request as verified
153 
154     :param change_request: the email change request
155     """
156     change_request.verified = True
157     change_request.save()
158 
159     process_email_change(change_request)
160 
161 
162 def confirm_email_change(change_request) -> None:
163     """
164     Mark the email change request as verified
165 
166     :param change_request: the email change request
167     """
168     change_request.confirmed = True
169     change_request.save()
170 
171     process_email_change(change_request)
172 
173 
174 def process_email_change(change_request) -> None:
175     """
176     Change the user's email address if the request was completed and
177     send the completion email
178 
179     :param change_request: the email change request
180     """
181     if not change_request.completed:
182         return
183 
184     member = change_request.member
185     member.email = change_request.email
186     member.save()
187 
188     emails.send_email_change_completion_message(change_request)
189 
190 
191 def execute_data_minimisation(dry_run=False, members=None) -> List[Member]:
192     """
193     Clean the profiles of members/users of whom the last membership ended
194     at least 31 days ago
195 
196     :param dry_run: does not really remove data if True
197     :param members: queryset of members to process, optional
198     :return: list of processed members
199     """
200     if not members:
201         members = Member.objects
202     members = (
203         members.annotate(membership_count=Count("membership"))
204         .exclude(
205             (
206                 Q(membership__until__isnull=True)
207                 | Q(membership__until__gt=timezone.now().date())
208             )
209             & Q(membership_count__gt=0)
210         )
211         .distinct()
212         .prefetch_related("membership_set", "profile")
213     )
214     deletion_period = timezone.now().date() - timezone.timedelta(days=31)
215     processed_members = []
216     for member in members:
217         if (
218             member.latest_membership is None
219             or member.latest_membership.until <= deletion_period
220         ):
221             processed_members.append(member)
222             profile = member.profile
223             profile.student_number = None
224             profile.phone_number = None
225             profile.address_street = None
226             profile.address_street2 = None
227             profile.address_postal_code = None
228             profile.address_city = None
229             profile.address_country = None
230             profile.birthday = None
231             profile.emergency_contact_phone_number = None
232             profile.emergency_contact = None
233             member.bank_accounts.all().delete()
234             if not dry_run:
235                 profile.save()
236 
237     return processed_members
238
```
--- 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/members/services.py b/website/members/services.py
--- a/website/members/services.py
+++ b/website/members/services.py
@@ -40,10 +40,13 @@
         name = membership.group.name
         if data.get(name):
             data[name]["periods"].append(period)
-            if data[name]["earliest"] > membership.since:
-                data[name]["earliest"] = membership.since
-            if data[name]["latest"] < membership.until:
-                data[name]["latest"] = membership.until
+            if data[name]["earliest"] > period["since"]:
+                data[name]["earliest"] = period["since"]
+            if period["until"] is None or (
+                data[name]["latest"] is not None
+                and data[name]["latest"] < period["until"]
+            ):
+                data[name]["latest"] = period["until"]
             data[name]["periods"].sort(key=lambda x: x["since"])
         else:
             data[name] = {
@@ -52,8 +55,8 @@
                 "name": name,
                 "periods": [period],
                 "url": settings.BASE_URL + membership.group.get_absolute_url(),
-                "earliest": membership.since,
-                "latest": membership.until,
+                "earliest": period["since"],
+                "latest": period["until"],
             }
     return data

{"golden_diff": "diff --git a/website/members/services.py b/website/members/services.py\n--- a/website/members/services.py\n+++ b/website/members/services.py\n@@ -40,10 +40,13 @@\n         name = membership.group.name\n         if data.get(name):\n             data[name][\"periods\"].append(period)\n-            if data[name][\"earliest\"] > membership.since:\n-                data[name][\"earliest\"] = membership.since\n-            if data[name][\"latest\"] < membership.until:\n-                data[name][\"latest\"] = membership.until\n+            if data[name][\"earliest\"] > period[\"since\"]:\n+                data[name][\"earliest\"] = period[\"since\"]\n+            if period[\"until\"] is None or (\n+                data[name][\"latest\"] is not None\n+                and data[name][\"latest\"] < period[\"until\"]\n+            ):\n+                data[name][\"latest\"] = period[\"until\"]\n             data[name][\"periods\"].sort(key=lambda x: x[\"since\"])\n         else:\n             data[name] = {\n@@ -52,8 +55,8 @@\n                 \"name\": name,\n                 \"periods\": [period],\n                 \"url\": settings.BASE_URL + membership.group.get_absolute_url(),\n-                \"earliest\": membership.since,\n-                \"latest\": membership.until,\n+                \"earliest\": period[\"since\"],\n+                \"latest\": period[\"until\"],\n             }\n     return data\n", "issue": "TypeError: '<' not supported between instances of 'datetime.date' and 'NoneType'\nSentry Issue: [CONCREXIT-3T](https://sentry.io/organizations/thalia/issues/1937285207/?referrer=github_integration)\n\n```\nTypeError: '<' not supported between instances of 'datetime.date' and 'NoneType'\n(5 additional frame(s) were not displayed)\n...\n  File \"django/views/generic/base.py\", line 98, in dispatch\n    return handler(request, *args, **kwargs)\n  File \"django/views/generic/detail.py\", line 107, in get\n    context = self.get_context_data(object=self.object)\n  File \"members/views.py\", line 181, in get_context_data\n    societies = member_societies(member)\n  File \"members/services.py\", line 91, in member_societies\n    societies = _member_group_memberships(\n  File \"members/services.py\", line 45, in _member_group_memberships\n    if data[name][\"latest\"] < membership.until:\n```\n", "before_files": [{"content": "\"\"\"Services defined in the members package\"\"\"\nfrom datetime import date\nfrom typing import Callable, List, Dict, Any\n\nfrom django.conf import settings\nfrom django.db.models import Q, Count\nfrom django.utils import timezone\nfrom django.utils.translation import gettext\n\nfrom members import emails\nfrom members.models import Membership, Member\nfrom utils.snippets import datetime_to_lectureyear\n\n\ndef _member_group_memberships(\n    member: Member, condition: Callable[[Membership], bool]\n) -> Dict[str, Any]:\n    \"\"\"\n    Determines the group membership of a user based on a condition\n    :return: Object with group memberships\n    \"\"\"\n    memberships = member.membergroupmembership_set.all()\n    data = {}\n\n    for membership in memberships:\n        if not condition(membership):\n            continue\n        period = {\n            \"since\": membership.since,\n            \"until\": membership.until,\n            \"chair\": membership.chair,\n        }\n\n        if hasattr(membership.group, \"board\"):\n            period[\"role\"] = membership.role\n\n        if membership.until is None and hasattr(membership.group, \"board\"):\n            period[\"until\"] = membership.group.board.until\n\n        name = membership.group.name\n        if data.get(name):\n            data[name][\"periods\"].append(period)\n            if data[name][\"earliest\"] > membership.since:\n                data[name][\"earliest\"] = membership.since\n            if data[name][\"latest\"] < membership.until:\n                data[name][\"latest\"] = membership.until\n            data[name][\"periods\"].sort(key=lambda x: x[\"since\"])\n        else:\n            data[name] = {\n                \"pk\": membership.group.pk,\n                \"active\": membership.group.active,\n                \"name\": name,\n                \"periods\": [period],\n                \"url\": settings.BASE_URL + membership.group.get_absolute_url(),\n                \"earliest\": membership.since,\n                \"latest\": membership.until,\n            }\n    return data\n\n\ndef member_achievements(member) -> List:\n    \"\"\"\n    Derives a list of achievements of a member\n    Committee and board memberships + mentorships\n    \"\"\"\n    achievements = _member_group_memberships(\n        member,\n        lambda membership: (\n            hasattr(membership.group, \"board\") or hasattr(membership.group, \"committee\")\n        ),\n    )\n\n    mentor_years = member.mentorship_set.all()\n    for mentor_year in mentor_years:\n        name = \"Mentor in {}\".format(mentor_year.year)\n        # Ensure mentorships appear last but are sorted\n        earliest = date.today()\n        earliest = earliest.replace(year=earliest.year + mentor_year.year)\n        if not achievements.get(name):\n            achievements[name] = {\n                \"name\": name,\n                \"earliest\": earliest,\n            }\n    return sorted(achievements.values(), key=lambda x: x[\"earliest\"])\n\n\ndef member_societies(member) -> List:\n    \"\"\"\n    Derives a list of societies a member was part of\n    \"\"\"\n    societies = _member_group_memberships(\n        member, lambda membership: (hasattr(membership.group, \"society\"))\n    )\n    return sorted(societies.values(), key=lambda x: x[\"earliest\"])\n\n\ndef gen_stats_member_type() -> Dict[str, int]:\n    \"\"\"\n    Generate a dictionary where every key is a member type with\n    the value being the number of current members of that type\n    \"\"\"\n\n    data = {}\n    for key, display in Membership.MEMBERSHIP_TYPES:\n        data[str(display)] = (\n            Membership.objects.filter(since__lte=date.today())\n            .filter(Q(until__isnull=True) | Q(until__gt=date.today()))\n            .filter(type=key)\n            .count()\n        )\n    return data\n\n\ndef gen_stats_year() -> Dict[str, Dict[str, int]]:\n    \"\"\"\n    Generate list with 6 entries, where each entry represents the total amount\n    of Thalia members in a year. The sixth element contains all the multi-year\n    students.\n    \"\"\"\n    stats_year = {}\n    current_year = datetime_to_lectureyear(date.today())\n\n    for i in range(5):\n        new = {}\n        for key, _ in Membership.MEMBERSHIP_TYPES:\n            new[key] = (\n                Membership.objects.filter(user__profile__starting_year=current_year - i)\n                .filter(since__lte=date.today())\n                .filter(Q(until__isnull=True) | Q(until__gt=date.today()))\n                .filter(type=key)\n                .count()\n            )\n        stats_year[str(current_year - i)] = new\n\n    # Add multi year members\n    new = {}\n    for key, _ in Membership.MEMBERSHIP_TYPES:\n        new[key] = (\n            Membership.objects.filter(user__profile__starting_year__lt=current_year - 4)\n            .filter(since__lte=date.today())\n            .filter(Q(until__isnull=True) | Q(until__gt=date.today()))\n            .filter(type=key)\n            .count()\n        )\n    stats_year[str(gettext(\"Older\"))] = new\n\n    return stats_year\n\n\ndef verify_email_change(change_request) -> None:\n    \"\"\"\n    Mark the email change request as verified\n\n    :param change_request: the email change request\n    \"\"\"\n    change_request.verified = True\n    change_request.save()\n\n    process_email_change(change_request)\n\n\ndef confirm_email_change(change_request) -> None:\n    \"\"\"\n    Mark the email change request as verified\n\n    :param change_request: the email change request\n    \"\"\"\n    change_request.confirmed = True\n    change_request.save()\n\n    process_email_change(change_request)\n\n\ndef process_email_change(change_request) -> None:\n    \"\"\"\n    Change the user's email address if the request was completed and\n    send the completion email\n\n    :param change_request: the email change request\n    \"\"\"\n    if not change_request.completed:\n        return\n\n    member = change_request.member\n    member.email = change_request.email\n    member.save()\n\n    emails.send_email_change_completion_message(change_request)\n\n\ndef execute_data_minimisation(dry_run=False, members=None) -> List[Member]:\n    \"\"\"\n    Clean the profiles of members/users of whom the last membership ended\n    at least 31 days ago\n\n    :param dry_run: does not really remove data if True\n    :param members: queryset of members to process, optional\n    :return: list of processed members\n    \"\"\"\n    if not members:\n        members = Member.objects\n    members = (\n        members.annotate(membership_count=Count(\"membership\"))\n        .exclude(\n            (\n                Q(membership__until__isnull=True)\n                | Q(membership__until__gt=timezone.now().date())\n            )\n            & Q(membership_count__gt=0)\n        )\n        .distinct()\n        .prefetch_related(\"membership_set\", \"profile\")\n    )\n    deletion_period = timezone.now().date() - timezone.timedelta(days=31)\n    processed_members = []\n    for member in members:\n        if (\n            member.latest_membership is None\n            or member.latest_membership.until <= deletion_period\n        ):\n            processed_members.append(member)\n            profile = member.profile\n            profile.student_number = None\n            profile.phone_number = None\n            profile.address_street = None\n            profile.address_street2 = None\n            profile.address_postal_code = None\n            profile.address_city = None\n            profile.address_country = None\n            profile.birthday = None\n            profile.emergency_contact_phone_number = None\n            profile.emergency_contact = None\n            member.bank_accounts.all().delete()\n            if not dry_run:\n                profile.save()\n\n    return processed_members\n", "path": "website/members/services.py"}], "after_files": [{"content": "\"\"\"Services defined in the members package\"\"\"\nfrom datetime import date\nfrom typing import Callable, List, Dict, Any\n\nfrom django.conf import settings\nfrom django.db.models import Q, Count\nfrom django.utils import timezone\nfrom django.utils.translation import gettext\n\nfrom members import emails\nfrom members.models import Membership, Member\nfrom utils.snippets import datetime_to_lectureyear\n\n\ndef _member_group_memberships(\n    member: Member, condition: Callable[[Membership], bool]\n) -> Dict[str, Any]:\n    \"\"\"\n    Determines the group membership of a user based on a condition\n    :return: Object with group memberships\n    \"\"\"\n    memberships = member.membergroupmembership_set.all()\n    data = {}\n\n    for membership in memberships:\n        if not condition(membership):\n            continue\n        period = {\n            \"since\": membership.since,\n            \"until\": membership.until,\n            \"chair\": membership.chair,\n        }\n\n        if hasattr(membership.group, \"board\"):\n            period[\"role\"] = membership.role\n\n        if membership.until is None and hasattr(membership.group, \"board\"):\n            period[\"until\"] = membership.group.board.until\n\n        name = membership.group.name\n        if data.get(name):\n            data[name][\"periods\"].append(period)\n            if data[name][\"earliest\"] > period[\"since\"]:\n                data[name][\"earliest\"] = period[\"since\"]\n            if period[\"until\"] is None or (\n                data[name][\"latest\"] is not None\n                and data[name][\"latest\"] < period[\"until\"]\n            ):\n                data[name][\"latest\"] = period[\"until\"]\n            data[name][\"periods\"].sort(key=lambda x: x[\"since\"])\n        else:\n            data[name] = {\n                \"pk\": membership.group.pk,\n                \"active\": membership.group.active,\n                \"name\": name,\n                \"periods\": [period],\n                \"url\": settings.BASE_URL + membership.group.get_absolute_url(),\n                \"earliest\": period[\"since\"],\n                \"latest\": period[\"until\"],\n            }\n    return data\n\n\ndef member_achievements(member) -> List:\n    \"\"\"\n    Derives a list of achievements of a member\n    Committee and board memberships + mentorships\n    \"\"\"\n    achievements = _member_group_memberships(\n        member,\n        lambda membership: (\n            hasattr(membership.group, \"board\") or hasattr(membership.group, \"committee\")\n        ),\n    )\n\n    mentor_years = member.mentorship_set.all()\n    for mentor_year in mentor_years:\n        name = \"Mentor in {}\".format(mentor_year.year)\n        # Ensure mentorships appear last but are sorted\n        earliest = date.today()\n        earliest = earliest.replace(year=earliest.year + mentor_year.year)\n        if not achievements.get(name):\n            achievements[name] = {\n                \"name\": name,\n                \"earliest\": earliest,\n            }\n    return sorted(achievements.values(), key=lambda x: x[\"earliest\"])\n\n\ndef member_societies(member) -> List:\n    \"\"\"\n    Derives a list of societies a member was part of\n    \"\"\"\n    societies = _member_group_memberships(\n        member, lambda membership: (hasattr(membership.group, \"society\"))\n    )\n    return sorted(societies.values(), key=lambda x: x[\"earliest\"])\n\n\ndef gen_stats_member_type() -> Dict[str, int]:\n    \"\"\"\n    Generate a dictionary where every key is a member type with\n    the value being the number of current members of that type\n    \"\"\"\n\n    data = {}\n    for key, display in Membership.MEMBERSHIP_TYPES:\n        data[str(display)] = (\n            Membership.objects.filter(since__lte=date.today())\n            .filter(Q(until__isnull=True) | Q(until__gt=date.today()))\n            .filter(type=key)\n            .count()\n        )\n    return data\n\n\ndef gen_stats_year() -> Dict[str, Dict[str, int]]:\n    \"\"\"\n    Generate list with 6 entries, where each entry represents the total amount\n    of Thalia members in a year. The sixth element contains all the multi-year\n    students.\n    \"\"\"\n    stats_year = {}\n    current_year = datetime_to_lectureyear(date.today())\n\n    for i in range(5):\n        new = {}\n        for key, _ in Membership.MEMBERSHIP_TYPES:\n            new[key] = (\n                Membership.objects.filter(user__profile__starting_year=current_year - i)\n                .filter(since__lte=date.today())\n                .filter(Q(until__isnull=True) | Q(until__gt=date.today()))\n                .filter(type=key)\n                .count()\n            )\n        stats_year[str(current_year - i)] = new\n\n    # Add multi year members\n    new = {}\n    for key, _ in Membership.MEMBERSHIP_TYPES:\n        new[key] = (\n            Membership.objects.filter(user__profile__starting_year__lt=current_year - 4)\n            .filter(since__lte=date.today())\n            .filter(Q(until__isnull=True) | Q(until__gt=date.today()))\n            .filter(type=key)\n            .count()\n        )\n    stats_year[str(gettext(\"Older\"))] = new\n\n    return stats_year\n\n\ndef verify_email_change(change_request) -> None:\n    \"\"\"\n    Mark the email change request as verified\n\n    :param change_request: the email change request\n    \"\"\"\n    change_request.verified = True\n    change_request.save()\n\n    process_email_change(change_request)\n\n\ndef confirm_email_change(change_request) -> None:\n    \"\"\"\n    Mark the email change request as verified\n\n    :param change_request: the email change request\n    \"\"\"\n    change_request.confirmed = True\n    change_request.save()\n\n    process_email_change(change_request)\n\n\ndef process_email_change(change_request) -> None:\n    \"\"\"\n    Change the user's email address if the request was completed and\n    send the completion email\n\n    :param change_request: the email change request\n    \"\"\"\n    if not change_request.completed:\n        return\n\n    member = change_request.member\n    member.email = change_request.email\n    member.save()\n\n    emails.send_email_change_completion_message(change_request)\n\n\ndef execute_data_minimisation(dry_run=False, members=None) -> List[Member]:\n    \"\"\"\n    Clean the profiles of members/users of whom the last membership ended\n    at least 31 days ago\n\n    :param dry_run: does not really remove data if True\n    :param members: queryset of members to process, optional\n    :return: list of processed members\n    \"\"\"\n    if not members:\n        members = Member.objects\n    members = (\n        members.annotate(membership_count=Count(\"membership\"))\n        .exclude(\n            (\n                Q(membership__until__isnull=True)\n                | Q(membership__until__gt=timezone.now().date())\n            )\n            & Q(membership_count__gt=0)\n        )\n        .distinct()\n        .prefetch_related(\"membership_set\", \"profile\")\n    )\n    deletion_period = timezone.now().date() - timezone.timedelta(days=31)\n    processed_members = []\n    for member in members:\n        if (\n            member.latest_membership is None\n            or member.latest_membership.until <= deletion_period\n        ):\n            processed_members.append(member)\n            profile = member.profile\n            profile.student_number = None\n            profile.phone_number = None\n            profile.address_street = None\n            profile.address_street2 = None\n            profile.address_postal_code = None\n            profile.address_city = None\n            profile.address_country = None\n            profile.birthday = None\n            profile.emergency_contact_phone_number = None\n            profile.emergency_contact = None\n            member.bank_accounts.all().delete()\n            if not dry_run:\n                profile.save()\n\n    return processed_members\n", "path": "website/members/services.py"}]}