We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Dataminimization breaks editing user profiles
### Describe the bug
I cannot edit the profiles and user information of users whose data has been minimized. 

### How to reproduce
Steps to reproduce the behaviour:
1. View member information with missing data fields (due to minimization)
2. Try to edit a value (e.g. email address)
3. Save form
4. Form cannot be saved because of data inconsistency errors

### Expected behaviour
Edits in incomplete profiles can still happen even though information is missing

### Screenshots
<img width="526" alt="image" src="https://user-images.githubusercontent.com/1576660/90406160-0cf79800-e0a5-11ea-8072-e8e311c58b75.png">


### Additional context
I was trying to change someones email address, so they could reset their password and renew their membership

Dataminimization breaks editing user profiles
### Describe the bug
I cannot edit the profiles and user information of users whose data has been minimized. 

### How to reproduce
Steps to reproduce the behaviour:
1. View member information with missing data fields (due to minimization)
2. Try to edit a value (e.g. email address)
3. Save form
4. Form cannot be saved because of data inconsistency errors

### Expected behaviour
Edits in incomplete profiles can still happen even though information is missing

### Screenshots
<img width="526" alt="image" src="https://user-images.githubusercontent.com/1576660/90406160-0cf79800-e0a5-11ea-8072-e8e311c58b75.png">


### Additional context
I was trying to change someones email address, so they could reset their password and renew their membership


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

{"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@@ -1,5 +1,5 @@\n \"\"\"Services defined in the members package\"\"\"\n-from datetime import date\n+from datetime import date, datetime\n from typing import Callable, List, Dict, Any\n \n from django.conf import settings\n@@ -225,12 +225,12 @@\n             profile = member.profile\n             profile.student_number = None\n             profile.phone_number = None\n-            profile.address_street = None\n+            profile.address_street = \"<removed> 1\"\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.address_postal_code = \"<removed>\"\n+            profile.address_city = \"<removed>\"\n+            profile.address_country = \"NL\"\n+            profile.birthday = datetime(1900, 1, 1)\n             profile.emergency_contact_phone_number = None\n             profile.emergency_contact = None\n             member.bank_accounts.all().delete()\n", "issue": "Dataminimization breaks editing user profiles\n### Describe the bug\r\nI cannot edit the profiles and user information of users whose data has been minimized. \r\n\r\n### How to reproduce\r\nSteps to reproduce the behaviour:\r\n1. View member information with missing data fields (due to minimization)\r\n2. Try to edit a value (e.g. email address)\r\n3. Save form\r\n4. Form cannot be saved because of data inconsistency errors\r\n\r\n### Expected behaviour\r\nEdits in incomplete profiles can still happen even though information is missing\r\n\r\n### Screenshots\r\n<img width=\"526\" alt=\"image\" src=\"https://user-images.githubusercontent.com/1576660/90406160-0cf79800-e0a5-11ea-8072-e8e311c58b75.png\">\r\n\r\n\r\n### Additional context\r\nI was trying to change someones email address, so they could reset their password and renew their membership\r\n\nDataminimization breaks editing user profiles\n### Describe the bug\r\nI cannot edit the profiles and user information of users whose data has been minimized. \r\n\r\n### How to reproduce\r\nSteps to reproduce the behaviour:\r\n1. View member information with missing data fields (due to minimization)\r\n2. Try to edit a value (e.g. email address)\r\n3. Save form\r\n4. Form cannot be saved because of data inconsistency errors\r\n\r\n### Expected behaviour\r\nEdits in incomplete profiles can still happen even though information is missing\r\n\r\n### Screenshots\r\n<img width=\"526\" alt=\"image\" src=\"https://user-images.githubusercontent.com/1576660/90406160-0cf79800-e0a5-11ea-8072-e8e311c58b75.png\">\r\n\r\n\r\n### Additional context\r\nI was trying to change someones email address, so they could reset their password and renew their membership\r\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\"] > 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"}], "after_files": [{"content": "\"\"\"Services defined in the members package\"\"\"\nfrom datetime import date, datetime\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 = \"<removed> 1\"\n            profile.address_street2 = None\n            profile.address_postal_code = \"<removed>\"\n            profile.address_city = \"<removed>\"\n            profile.address_country = \"NL\"\n            profile.birthday = datetime(1900, 1, 1)\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"}]}