We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Log old email when changing an order's email address
… because otherwise it's completely lost.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `src/pretix/control/logdisplay.py`
Content:
```
1 import json
2 from decimal import Decimal
3 
4 from django.dispatch import receiver
5 from django.utils import formats
6 from django.utils.translation import ugettext_lazy as _
7 
8 from pretix.base.models import Event, ItemVariation, LogEntry
9 from pretix.base.signals import logentry_display
10 
11 
12 def _display_order_changed(event: Event, logentry: LogEntry):
13     data = json.loads(logentry.data)
14 
15     text = _('The order has been changed:')
16     if logentry.action_type == 'pretix.event.order.changed.item':
17         old_item = str(event.items.get(pk=data['old_item']))
18         if data['old_variation']:
19             old_item += ' - ' + str(event.itemvariations.get(pk=data['old_variation']))
20         new_item = str(event.items.get(pk=data['new_item']))
21         if data['new_variation']:
22             new_item += ' - ' + str(event.itemvariations.get(pk=data['new_variation']))
23         return text + ' ' + _('{old_item} ({old_price} {currency}) changed to {new_item} ({new_price} {currency}).').format(
24             old_item=old_item, new_item=new_item,
25             old_price=formats.localize(Decimal(data['old_price'])),
26             new_price=formats.localize(Decimal(data['new_price'])),
27             currency=event.currency
28         )
29     elif logentry.action_type == 'pretix.event.order.changed.price':
30         return text + ' ' + _('Price of a position changed from {old_price} {currency} to {new_price} {currency}.').format(
31             old_price=formats.localize(Decimal(data['old_price'])),
32             new_price=formats.localize(Decimal(data['new_price'])),
33             currency=event.currency
34         )
35     elif logentry.action_type == 'pretix.event.order.changed.cancel':
36         old_item = str(event.items.get(pk=data['old_item']))
37         if data['old_variation']:
38             old_item += ' - ' + str(ItemVariation.objects.get(pk=data['old_variation']))
39         return text + ' ' + _('{old_item} ({old_price} {currency}) removed.').format(
40             old_item=old_item,
41             old_price=formats.localize(Decimal(data['old_price'])),
42             currency=event.currency
43         )
44 
45 
46 @receiver(signal=logentry_display, dispatch_uid="pretixcontrol_logentry_display")
47 def pretixcontrol_logentry_display(sender: Event, logentry: LogEntry, **kwargs):
48     plains = {
49         'pretix.event.order.modified': _('The order details have been modified.'),
50         'pretix.event.order.unpaid': _('The order has been marked as unpaid.'),
51         'pretix.event.order.resend': _('The link to the order detail page has been resent to the user.'),
52         'pretix.event.order.expirychanged': _('The order\'s expiry date has been changed.'),
53         'pretix.event.order.expired': _('The order has been marked as expired.'),
54         'pretix.event.order.paid': _('The order has been marked as paid.'),
55         'pretix.event.order.refunded': _('The order has been refunded.'),
56         'pretix.event.order.canceled': _('The order has been canceled.'),
57         'pretix.event.order.placed': _('The order has been created.'),
58         'pretix.event.order.invoice.generated': _('The invoice has been generated.'),
59         'pretix.event.order.invoice.regenerated': _('The invoice has been regenerated.'),
60         'pretix.event.order.invoice.reissued': _('The invoice has been reissued.'),
61         'pretix.event.order.comment': _('The order\'s internal comment has been updated.'),
62         'pretix.event.order.contact.changed': _('The email address has been changed.'),
63         'pretix.event.order.payment.changed': _('The payment method has been changed.'),
64         'pretix.event.order.expire_warning_sent': _('An email has been sent with a warning that the order is about to expire.'),
65         'pretix.user.settings.2fa.enabled': _('Two-factor authentication has been enabled.'),
66         'pretix.user.settings.2fa.disabled': _('Two-factor authentication has been disabled.'),
67         'pretix.user.settings.2fa.regenemergency': _('Your two-factor emergency codes have been regenerated.'),
68         'pretix.control.auth.user.forgot_password.mail_sent': _('Password reset mail sent.'),
69         'pretix.control.auth.user.forgot_password.recovered': _('The password has been reset.')
70 
71     }
72     if logentry.action_type in plains:
73         return plains[logentry.action_type]
74 
75     if logentry.action_type.startswith('pretix.event.order.changed'):
76         return _display_order_changed(sender, logentry)
77 
78     if logentry.action_type == 'pretix.user.settings.2fa.device.added':
79         data = json.loads(logentry.data)
80         return _('A new two-factor authentication device "{name}" has been added to your account.').format(
81             name=data['name']
82         )
83     if logentry.action_type == 'pretix.user.settings.2fa.device.deleted':
84         data = json.loads(logentry.data)
85         return _('The two-factor authentication device "{name}" has been removed from your account.').format(
86             name=data['name']
87         )
88     if logentry.action_type == 'pretix.user.settings.changed':
89         data = json.loads(logentry.data)
90         text = str(_('Your account settings have been changed.'))
91         if 'email' in data:
92             text = text + ' ' + str(_('Your email address has been changed to {email}.').format(email=data['email']))
93         if 'new_pw' in data:
94             text = text + ' ' + str(_('Your password has been changed.'))
95         return text
96
```
--- 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/src/pretix/control/logdisplay.py b/src/pretix/control/logdisplay.py
--- a/src/pretix/control/logdisplay.py
+++ b/src/pretix/control/logdisplay.py
@@ -59,7 +59,6 @@
         'pretix.event.order.invoice.regenerated': _('The invoice has been regenerated.'),
         'pretix.event.order.invoice.reissued': _('The invoice has been reissued.'),
         'pretix.event.order.comment': _('The order\'s internal comment has been updated.'),
-        'pretix.event.order.contact.changed': _('The email address has been changed.'),
         'pretix.event.order.payment.changed': _('The payment method has been changed.'),
         'pretix.event.order.expire_warning_sent': _('An email has been sent with a warning that the order is about to expire.'),
         'pretix.user.settings.2fa.enabled': _('Two-factor authentication has been enabled.'),
@@ -75,6 +74,13 @@
     if logentry.action_type.startswith('pretix.event.order.changed'):
         return _display_order_changed(sender, logentry)
 
+    if logentry.action_type.startswith('pretix.event.order.contact.changed'):
+        data = json.loads(logentry.data)
+        return _('The email address has been changed from "{old}" to "{new}".').format(
+            old=data['old_email'],
+            new=data['new_email'],
+        )
+
     if logentry.action_type == 'pretix.user.settings.2fa.device.added':
         data = json.loads(logentry.data)
         return _('A new two-factor authentication device "{name}" has been added to your account.').format(

{"golden_diff": "diff --git a/src/pretix/control/logdisplay.py b/src/pretix/control/logdisplay.py\n--- a/src/pretix/control/logdisplay.py\n+++ b/src/pretix/control/logdisplay.py\n@@ -59,7 +59,6 @@\n         'pretix.event.order.invoice.regenerated': _('The invoice has been regenerated.'),\n         'pretix.event.order.invoice.reissued': _('The invoice has been reissued.'),\n         'pretix.event.order.comment': _('The order\\'s internal comment has been updated.'),\n-        'pretix.event.order.contact.changed': _('The email address has been changed.'),\n         'pretix.event.order.payment.changed': _('The payment method has been changed.'),\n         'pretix.event.order.expire_warning_sent': _('An email has been sent with a warning that the order is about to expire.'),\n         'pretix.user.settings.2fa.enabled': _('Two-factor authentication has been enabled.'),\n@@ -75,6 +74,13 @@\n     if logentry.action_type.startswith('pretix.event.order.changed'):\n         return _display_order_changed(sender, logentry)\n \n+    if logentry.action_type.startswith('pretix.event.order.contact.changed'):\n+        data = json.loads(logentry.data)\n+        return _('The email address has been changed from \"{old}\" to \"{new}\".').format(\n+            old=data['old_email'],\n+            new=data['new_email'],\n+        )\n+\n     if logentry.action_type == 'pretix.user.settings.2fa.device.added':\n         data = json.loads(logentry.data)\n         return _('A new two-factor authentication device \"{name}\" has been added to your account.').format(\n", "issue": "Log old email when changing an order's email address\n\u2026 because otherwise it's completely lost.\n", "before_files": [{"content": "import json\nfrom decimal import Decimal\n\nfrom django.dispatch import receiver\nfrom django.utils import formats\nfrom django.utils.translation import ugettext_lazy as _\n\nfrom pretix.base.models import Event, ItemVariation, LogEntry\nfrom pretix.base.signals import logentry_display\n\n\ndef _display_order_changed(event: Event, logentry: LogEntry):\n    data = json.loads(logentry.data)\n\n    text = _('The order has been changed:')\n    if logentry.action_type == 'pretix.event.order.changed.item':\n        old_item = str(event.items.get(pk=data['old_item']))\n        if data['old_variation']:\n            old_item += ' - ' + str(event.itemvariations.get(pk=data['old_variation']))\n        new_item = str(event.items.get(pk=data['new_item']))\n        if data['new_variation']:\n            new_item += ' - ' + str(event.itemvariations.get(pk=data['new_variation']))\n        return text + ' ' + _('{old_item} ({old_price} {currency}) changed to {new_item} ({new_price} {currency}).').format(\n            old_item=old_item, new_item=new_item,\n            old_price=formats.localize(Decimal(data['old_price'])),\n            new_price=formats.localize(Decimal(data['new_price'])),\n            currency=event.currency\n        )\n    elif logentry.action_type == 'pretix.event.order.changed.price':\n        return text + ' ' + _('Price of a position changed from {old_price} {currency} to {new_price} {currency}.').format(\n            old_price=formats.localize(Decimal(data['old_price'])),\n            new_price=formats.localize(Decimal(data['new_price'])),\n            currency=event.currency\n        )\n    elif logentry.action_type == 'pretix.event.order.changed.cancel':\n        old_item = str(event.items.get(pk=data['old_item']))\n        if data['old_variation']:\n            old_item += ' - ' + str(ItemVariation.objects.get(pk=data['old_variation']))\n        return text + ' ' + _('{old_item} ({old_price} {currency}) removed.').format(\n            old_item=old_item,\n            old_price=formats.localize(Decimal(data['old_price'])),\n            currency=event.currency\n        )\n\n\n@receiver(signal=logentry_display, dispatch_uid=\"pretixcontrol_logentry_display\")\ndef pretixcontrol_logentry_display(sender: Event, logentry: LogEntry, **kwargs):\n    plains = {\n        'pretix.event.order.modified': _('The order details have been modified.'),\n        'pretix.event.order.unpaid': _('The order has been marked as unpaid.'),\n        'pretix.event.order.resend': _('The link to the order detail page has been resent to the user.'),\n        'pretix.event.order.expirychanged': _('The order\\'s expiry date has been changed.'),\n        'pretix.event.order.expired': _('The order has been marked as expired.'),\n        'pretix.event.order.paid': _('The order has been marked as paid.'),\n        'pretix.event.order.refunded': _('The order has been refunded.'),\n        'pretix.event.order.canceled': _('The order has been canceled.'),\n        'pretix.event.order.placed': _('The order has been created.'),\n        'pretix.event.order.invoice.generated': _('The invoice has been generated.'),\n        'pretix.event.order.invoice.regenerated': _('The invoice has been regenerated.'),\n        'pretix.event.order.invoice.reissued': _('The invoice has been reissued.'),\n        'pretix.event.order.comment': _('The order\\'s internal comment has been updated.'),\n        'pretix.event.order.contact.changed': _('The email address has been changed.'),\n        'pretix.event.order.payment.changed': _('The payment method has been changed.'),\n        'pretix.event.order.expire_warning_sent': _('An email has been sent with a warning that the order is about to expire.'),\n        'pretix.user.settings.2fa.enabled': _('Two-factor authentication has been enabled.'),\n        'pretix.user.settings.2fa.disabled': _('Two-factor authentication has been disabled.'),\n        'pretix.user.settings.2fa.regenemergency': _('Your two-factor emergency codes have been regenerated.'),\n        'pretix.control.auth.user.forgot_password.mail_sent': _('Password reset mail sent.'),\n        'pretix.control.auth.user.forgot_password.recovered': _('The password has been reset.')\n\n    }\n    if logentry.action_type in plains:\n        return plains[logentry.action_type]\n\n    if logentry.action_type.startswith('pretix.event.order.changed'):\n        return _display_order_changed(sender, logentry)\n\n    if logentry.action_type == 'pretix.user.settings.2fa.device.added':\n        data = json.loads(logentry.data)\n        return _('A new two-factor authentication device \"{name}\" has been added to your account.').format(\n            name=data['name']\n        )\n    if logentry.action_type == 'pretix.user.settings.2fa.device.deleted':\n        data = json.loads(logentry.data)\n        return _('The two-factor authentication device \"{name}\" has been removed from your account.').format(\n            name=data['name']\n        )\n    if logentry.action_type == 'pretix.user.settings.changed':\n        data = json.loads(logentry.data)\n        text = str(_('Your account settings have been changed.'))\n        if 'email' in data:\n            text = text + ' ' + str(_('Your email address has been changed to {email}.').format(email=data['email']))\n        if 'new_pw' in data:\n            text = text + ' ' + str(_('Your password has been changed.'))\n        return text\n", "path": "src/pretix/control/logdisplay.py"}], "after_files": [{"content": "import json\nfrom decimal import Decimal\n\nfrom django.dispatch import receiver\nfrom django.utils import formats\nfrom django.utils.translation import ugettext_lazy as _\n\nfrom pretix.base.models import Event, ItemVariation, LogEntry\nfrom pretix.base.signals import logentry_display\n\n\ndef _display_order_changed(event: Event, logentry: LogEntry):\n    data = json.loads(logentry.data)\n\n    text = _('The order has been changed:')\n    if logentry.action_type == 'pretix.event.order.changed.item':\n        old_item = str(event.items.get(pk=data['old_item']))\n        if data['old_variation']:\n            old_item += ' - ' + str(event.itemvariations.get(pk=data['old_variation']))\n        new_item = str(event.items.get(pk=data['new_item']))\n        if data['new_variation']:\n            new_item += ' - ' + str(event.itemvariations.get(pk=data['new_variation']))\n        return text + ' ' + _('{old_item} ({old_price} {currency}) changed to {new_item} ({new_price} {currency}).').format(\n            old_item=old_item, new_item=new_item,\n            old_price=formats.localize(Decimal(data['old_price'])),\n            new_price=formats.localize(Decimal(data['new_price'])),\n            currency=event.currency\n        )\n    elif logentry.action_type == 'pretix.event.order.changed.price':\n        return text + ' ' + _('Price of a position changed from {old_price} {currency} to {new_price} {currency}.').format(\n            old_price=formats.localize(Decimal(data['old_price'])),\n            new_price=formats.localize(Decimal(data['new_price'])),\n            currency=event.currency\n        )\n    elif logentry.action_type == 'pretix.event.order.changed.cancel':\n        old_item = str(event.items.get(pk=data['old_item']))\n        if data['old_variation']:\n            old_item += ' - ' + str(ItemVariation.objects.get(pk=data['old_variation']))\n        return text + ' ' + _('{old_item} ({old_price} {currency}) removed.').format(\n            old_item=old_item,\n            old_price=formats.localize(Decimal(data['old_price'])),\n            currency=event.currency\n        )\n\n\n@receiver(signal=logentry_display, dispatch_uid=\"pretixcontrol_logentry_display\")\ndef pretixcontrol_logentry_display(sender: Event, logentry: LogEntry, **kwargs):\n    plains = {\n        'pretix.event.order.modified': _('The order details have been modified.'),\n        'pretix.event.order.unpaid': _('The order has been marked as unpaid.'),\n        'pretix.event.order.resend': _('The link to the order detail page has been resent to the user.'),\n        'pretix.event.order.expirychanged': _('The order\\'s expiry date has been changed.'),\n        'pretix.event.order.expired': _('The order has been marked as expired.'),\n        'pretix.event.order.paid': _('The order has been marked as paid.'),\n        'pretix.event.order.refunded': _('The order has been refunded.'),\n        'pretix.event.order.canceled': _('The order has been canceled.'),\n        'pretix.event.order.placed': _('The order has been created.'),\n        'pretix.event.order.invoice.generated': _('The invoice has been generated.'),\n        'pretix.event.order.invoice.regenerated': _('The invoice has been regenerated.'),\n        'pretix.event.order.invoice.reissued': _('The invoice has been reissued.'),\n        'pretix.event.order.comment': _('The order\\'s internal comment has been updated.'),\n        'pretix.event.order.payment.changed': _('The payment method has been changed.'),\n        'pretix.event.order.expire_warning_sent': _('An email has been sent with a warning that the order is about to expire.'),\n        'pretix.user.settings.2fa.enabled': _('Two-factor authentication has been enabled.'),\n        'pretix.user.settings.2fa.disabled': _('Two-factor authentication has been disabled.'),\n        'pretix.user.settings.2fa.regenemergency': _('Your two-factor emergency codes have been regenerated.'),\n        'pretix.control.auth.user.forgot_password.mail_sent': _('Password reset mail sent.'),\n        'pretix.control.auth.user.forgot_password.recovered': _('The password has been reset.')\n\n    }\n    if logentry.action_type in plains:\n        return plains[logentry.action_type]\n\n    if logentry.action_type.startswith('pretix.event.order.changed'):\n        return _display_order_changed(sender, logentry)\n\n    if logentry.action_type.startswith('pretix.event.order.contact.changed'):\n        data = json.loads(logentry.data)\n        return _('The email address has been changed from \"{old}\" to \"{new}\".').format(\n            old=data['old_email'],\n            new=data['new_email'],\n        )\n\n    if logentry.action_type == 'pretix.user.settings.2fa.device.added':\n        data = json.loads(logentry.data)\n        return _('A new two-factor authentication device \"{name}\" has been added to your account.').format(\n            name=data['name']\n        )\n    if logentry.action_type == 'pretix.user.settings.2fa.device.deleted':\n        data = json.loads(logentry.data)\n        return _('The two-factor authentication device \"{name}\" has been removed from your account.').format(\n            name=data['name']\n        )\n    if logentry.action_type == 'pretix.user.settings.changed':\n        data = json.loads(logentry.data)\n        text = str(_('Your account settings have been changed.'))\n        if 'email' in data:\n            text = text + ' ' + str(_('Your email address has been changed to {email}.').format(email=data['email']))\n        if 'new_pw' in data:\n            text = text + ' ' + str(_('Your password has been changed.'))\n        return text\n", "path": "src/pretix/control/logdisplay.py"}]}