We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
resetting schedule to an old version can make submissions unavailable
1. create submission: sub1
2. publish a schedule containing only sub1: ScheduleA
3. create submission: sub2
4. publish a schedule containing both sub1 and sub2: ScheduleB
5. reset the current draft to ScheduleA

Sub2 is not part of the current draft and should therefore be visible in the list on the right. But the list is empty. Resetting the draft to ScheduleB restores the talk.
resetting schedule to an old version can make submissions unavailable
1. create submission: sub1
2. publish a schedule containing only sub1: ScheduleA
3. create submission: sub2
4. publish a schedule containing both sub1 and sub2: ScheduleB
5. reset the current draft to ScheduleA

Sub2 is not part of the current draft and should therefore be visible in the list on the right. But the list is empty. Resetting the draft to ScheduleB restores the talk.
resetting schedule to an old version can make submissions unavailable
1. create submission: sub1
2. publish a schedule containing only sub1: ScheduleA
3. create submission: sub2
4. publish a schedule containing both sub1 and sub2: ScheduleB
5. reset the current draft to ScheduleA

Sub2 is not part of the current draft and should therefore be visible in the list on the right. But the list is empty. Resetting the draft to ScheduleB restores the talk.

--- 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/pretalx/schedule/models/schedule.py`
Content:
```
1 from collections import defaultdict
2 from contextlib import suppress
3 
4 import pytz
5 from django.db import models, transaction
6 from django.template.loader import get_template
7 from django.utils.functional import cached_property
8 from django.utils.timezone import now, override as tzoverride
9 from django.utils.translation import override, ugettext_lazy as _
10 
11 from pretalx.common.mixins import LogMixin
12 from pretalx.mail.models import QueuedMail
13 from pretalx.person.models import User
14 from pretalx.submission.models import SubmissionStates
15 
16 
17 class Schedule(LogMixin, models.Model):
18     event = models.ForeignKey(
19         to='event.Event',
20         on_delete=models.PROTECT,
21         related_name='schedules',
22     )
23     version = models.CharField(
24         max_length=200,
25         null=True, blank=True,
26     )
27     published = models.DateTimeField(
28         null=True, blank=True
29     )
30 
31     class Meta:
32         ordering = ('-published', )
33         unique_together = (('event', 'version'), )
34 
35     @transaction.atomic
36     def freeze(self, name, user=None):
37         from pretalx.schedule.models import TalkSlot
38         if self.version:
39             raise Exception(f'Cannot freeze schedule version: already versioned as "{self.version}".')
40 
41         self.version = name
42         self.published = now()
43         self.save(update_fields=['published', 'version'])
44         self.log_action('pretalx.schedule.release', person=user, orga=True)
45 
46         wip_schedule = Schedule.objects.create(event=self.event)
47 
48         # Set visibility
49         self.talks.filter(
50             start__isnull=False, submission__state=SubmissionStates.CONFIRMED, is_visible=False
51         ).update(is_visible=True)
52         self.talks.filter(is_visible=True).exclude(
53             start__isnull=False, submission__state=SubmissionStates.CONFIRMED
54         ).update(is_visible=False)
55 
56         talks = []
57         for talk in self.talks.select_related('submission', 'room').all():
58             talks.append(talk.copy_to_schedule(wip_schedule, save=False))
59         TalkSlot.objects.bulk_create(talks)
60 
61         self.notify_speakers()
62 
63         with suppress(AttributeError):
64             del wip_schedule.event.wip_schedule
65 
66         return self, wip_schedule
67 
68     def unfreeze(self, user=None):
69         from pretalx.schedule.models import TalkSlot
70         if not self.version:
71             raise Exception('Cannot unfreeze schedule version: not released yet.')
72         self.event.wip_schedule.talks.all().delete()
73         self.event.wip_schedule.delete()
74         wip_schedule = Schedule.objects.create(event=self.event)
75         talks = []
76         for talk in self.talks.all():
77             talks.append(talk.copy_to_schedule(wip_schedule, save=False))
78         TalkSlot.objects.bulk_create(talks)
79         return self, wip_schedule
80 
81     @cached_property
82     def scheduled_talks(self):
83         return self.talks.filter(
84             room__isnull=False,
85             start__isnull=False,
86         )
87 
88     @cached_property
89     def previous_schedule(self):
90         return self.event.schedules.filter(published__lt=self.published).order_by('-published').first()
91 
92     @cached_property
93     def changes(self):
94         tz = pytz.timezone(self.event.timezone)
95         result = {
96             'count': 0,
97             'action': 'update',
98             'new_talks': [],
99             'canceled_talks': [],
100             'moved_talks': [],
101         }
102         if not self.previous_schedule:
103             result['action'] = 'create'
104             return result
105 
106         new_slots = set(
107             talk
108             for talk in self.talks.select_related('submission', 'submission__event', 'room').all()
109             if talk.is_visible
110         )
111         old_slots = set(
112             talk
113             for talk in self.previous_schedule.talks.select_related('submission', 'submission__event', 'room').all()
114             if talk.is_visible
115         )
116 
117         new_submissions = set(talk.submission for talk in new_slots)
118         old_submissions = set(talk.submission for talk in old_slots)
119 
120         new_slot_by_submission = {talk.submission: talk for talk in new_slots}
121         old_slot_by_submission = {talk.submission: talk for talk in old_slots}
122 
123         result['new_talks'] = [new_slot_by_submission.get(s) for s in new_submissions - old_submissions]
124         result['canceled_talks'] = [old_slot_by_submission.get(s) for s in old_submissions - new_submissions]
125 
126         for submission in (new_submissions & old_submissions):
127             old_slot = old_slot_by_submission.get(submission)
128             new_slot = new_slot_by_submission.get(submission)
129             if new_slot.room and not old_slot.room:
130                 result['new_talks'].append(new_slot)
131             elif not new_slot.room and old_slot.room:
132                 result['canceled_talks'].append(new_slot)
133             elif old_slot.start != new_slot.start or old_slot.room != new_slot.room:
134                 if new_slot.room:
135                     result['moved_talks'].append({
136                         'submission': submission,
137                         'old_start': old_slot.start.astimezone(tz),
138                         'new_start': new_slot.start.astimezone(tz),
139                         'old_room': old_slot.room.name,
140                         'new_room': new_slot.room.name,
141                     })
142 
143         result['count'] = len(result['new_talks']) + len(result['canceled_talks']) + len(result['moved_talks'])
144         return result
145 
146     def notify_speakers(self):
147         tz = pytz.timezone(self.event.timezone)
148         speakers = defaultdict(lambda: {'create': [], 'update': []})
149         if self.changes['action'] == 'create':
150             speakers = {
151                 speaker: {'create': self.talks.filter(submission__speakers=speaker), 'update': []}
152                 for speaker in User.objects.filter(submissions__slots__schedule=self)
153             }
154         else:
155             if self.changes['count'] == len(self.changes['canceled_talks']):
156                 return
157 
158             for new_talk in self.changes['new_talks']:
159                 for speaker in new_talk.submission.speakers.all():
160                     speakers[speaker]['create'].append(new_talk)
161             for moved_talk in self.changes['moved_talks']:
162                 for speaker in moved_talk['submission'].speakers.all():
163                     speakers[speaker]['update'].append(moved_talk)
164         for speaker in speakers:
165             with override(speaker.locale), tzoverride(tz):
166                 text = get_template('schedule/speaker_notification.txt').render(
167                     {'speaker': speaker, **speakers[speaker]}
168                 )
169             QueuedMail.objects.create(
170                 event=self.event,
171                 to=speaker.email,
172                 reply_to=self.event.email,
173                 subject=_('[{event}] New schedule!').format(event=self.event.slug),
174                 text=text
175             )
176 
177     def __str__(self) -> str:
178         return str(self.version) or _(f'WIP Schedule for {self.event}')
179
```
--- 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/pretalx/schedule/models/schedule.py b/src/pretalx/schedule/models/schedule.py
--- a/src/pretalx/schedule/models/schedule.py
+++ b/src/pretalx/schedule/models/schedule.py
@@ -69,13 +69,25 @@
         from pretalx.schedule.models import TalkSlot
         if not self.version:
             raise Exception('Cannot unfreeze schedule version: not released yet.')
+
+        # collect all talks, which have been added since this schedule (#72)
+        submission_ids = self.talks.all().values_list('submission_id', flat=True)
+        talks = self.event.wip_schedule.talks \
+            .exclude(submission_id__in=submission_ids) \
+            .union(self.talks.all())
+
+        wip_schedule = Schedule.objects.create(event=self.event)
+        new_talks = []
+        for talk in talks:
+            new_talks.append(talk.copy_to_schedule(wip_schedule, save=False))
+        TalkSlot.objects.bulk_create(new_talks)
+
         self.event.wip_schedule.talks.all().delete()
         self.event.wip_schedule.delete()
-        wip_schedule = Schedule.objects.create(event=self.event)
-        talks = []
-        for talk in self.talks.all():
-            talks.append(talk.copy_to_schedule(wip_schedule, save=False))
-        TalkSlot.objects.bulk_create(talks)
+
+        with suppress(AttributeError):
+            del wip_schedule.event.wip_schedule
+
         return self, wip_schedule
 
     @cached_property

{"golden_diff": "diff --git a/src/pretalx/schedule/models/schedule.py b/src/pretalx/schedule/models/schedule.py\n--- a/src/pretalx/schedule/models/schedule.py\n+++ b/src/pretalx/schedule/models/schedule.py\n@@ -69,13 +69,25 @@\n         from pretalx.schedule.models import TalkSlot\n         if not self.version:\n             raise Exception('Cannot unfreeze schedule version: not released yet.')\n+\n+        # collect all talks, which have been added since this schedule (#72)\n+        submission_ids = self.talks.all().values_list('submission_id', flat=True)\n+        talks = self.event.wip_schedule.talks \\\n+            .exclude(submission_id__in=submission_ids) \\\n+            .union(self.talks.all())\n+\n+        wip_schedule = Schedule.objects.create(event=self.event)\n+        new_talks = []\n+        for talk in talks:\n+            new_talks.append(talk.copy_to_schedule(wip_schedule, save=False))\n+        TalkSlot.objects.bulk_create(new_talks)\n+\n         self.event.wip_schedule.talks.all().delete()\n         self.event.wip_schedule.delete()\n-        wip_schedule = Schedule.objects.create(event=self.event)\n-        talks = []\n-        for talk in self.talks.all():\n-            talks.append(talk.copy_to_schedule(wip_schedule, save=False))\n-        TalkSlot.objects.bulk_create(talks)\n+\n+        with suppress(AttributeError):\n+            del wip_schedule.event.wip_schedule\n+\n         return self, wip_schedule\n \n     @cached_property\n", "issue": "resetting schedule to an old version can make submissions unavailable\n1. create submission: sub1\r\n2. publish a schedule containing only sub1: ScheduleA\r\n3. create submission: sub2\r\n4. publish a schedule containing both sub1 and sub2: ScheduleB\r\n5. reset the current draft to ScheduleA\r\n\r\nSub2 is not part of the current draft and should therefore be visible in the list on the right. But the list is empty. Resetting the draft to ScheduleB restores the talk.\nresetting schedule to an old version can make submissions unavailable\n1. create submission: sub1\r\n2. publish a schedule containing only sub1: ScheduleA\r\n3. create submission: sub2\r\n4. publish a schedule containing both sub1 and sub2: ScheduleB\r\n5. reset the current draft to ScheduleA\r\n\r\nSub2 is not part of the current draft and should therefore be visible in the list on the right. But the list is empty. Resetting the draft to ScheduleB restores the talk.\nresetting schedule to an old version can make submissions unavailable\n1. create submission: sub1\r\n2. publish a schedule containing only sub1: ScheduleA\r\n3. create submission: sub2\r\n4. publish a schedule containing both sub1 and sub2: ScheduleB\r\n5. reset the current draft to ScheduleA\r\n\r\nSub2 is not part of the current draft and should therefore be visible in the list on the right. But the list is empty. Resetting the draft to ScheduleB restores the talk.\n", "before_files": [{"content": "from collections import defaultdict\nfrom contextlib import suppress\n\nimport pytz\nfrom django.db import models, transaction\nfrom django.template.loader import get_template\nfrom django.utils.functional import cached_property\nfrom django.utils.timezone import now, override as tzoverride\nfrom django.utils.translation import override, ugettext_lazy as _\n\nfrom pretalx.common.mixins import LogMixin\nfrom pretalx.mail.models import QueuedMail\nfrom pretalx.person.models import User\nfrom pretalx.submission.models import SubmissionStates\n\n\nclass Schedule(LogMixin, models.Model):\n    event = models.ForeignKey(\n        to='event.Event',\n        on_delete=models.PROTECT,\n        related_name='schedules',\n    )\n    version = models.CharField(\n        max_length=200,\n        null=True, blank=True,\n    )\n    published = models.DateTimeField(\n        null=True, blank=True\n    )\n\n    class Meta:\n        ordering = ('-published', )\n        unique_together = (('event', 'version'), )\n\n    @transaction.atomic\n    def freeze(self, name, user=None):\n        from pretalx.schedule.models import TalkSlot\n        if self.version:\n            raise Exception(f'Cannot freeze schedule version: already versioned as \"{self.version}\".')\n\n        self.version = name\n        self.published = now()\n        self.save(update_fields=['published', 'version'])\n        self.log_action('pretalx.schedule.release', person=user, orga=True)\n\n        wip_schedule = Schedule.objects.create(event=self.event)\n\n        # Set visibility\n        self.talks.filter(\n            start__isnull=False, submission__state=SubmissionStates.CONFIRMED, is_visible=False\n        ).update(is_visible=True)\n        self.talks.filter(is_visible=True).exclude(\n            start__isnull=False, submission__state=SubmissionStates.CONFIRMED\n        ).update(is_visible=False)\n\n        talks = []\n        for talk in self.talks.select_related('submission', 'room').all():\n            talks.append(talk.copy_to_schedule(wip_schedule, save=False))\n        TalkSlot.objects.bulk_create(talks)\n\n        self.notify_speakers()\n\n        with suppress(AttributeError):\n            del wip_schedule.event.wip_schedule\n\n        return self, wip_schedule\n\n    def unfreeze(self, user=None):\n        from pretalx.schedule.models import TalkSlot\n        if not self.version:\n            raise Exception('Cannot unfreeze schedule version: not released yet.')\n        self.event.wip_schedule.talks.all().delete()\n        self.event.wip_schedule.delete()\n        wip_schedule = Schedule.objects.create(event=self.event)\n        talks = []\n        for talk in self.talks.all():\n            talks.append(talk.copy_to_schedule(wip_schedule, save=False))\n        TalkSlot.objects.bulk_create(talks)\n        return self, wip_schedule\n\n    @cached_property\n    def scheduled_talks(self):\n        return self.talks.filter(\n            room__isnull=False,\n            start__isnull=False,\n        )\n\n    @cached_property\n    def previous_schedule(self):\n        return self.event.schedules.filter(published__lt=self.published).order_by('-published').first()\n\n    @cached_property\n    def changes(self):\n        tz = pytz.timezone(self.event.timezone)\n        result = {\n            'count': 0,\n            'action': 'update',\n            'new_talks': [],\n            'canceled_talks': [],\n            'moved_talks': [],\n        }\n        if not self.previous_schedule:\n            result['action'] = 'create'\n            return result\n\n        new_slots = set(\n            talk\n            for talk in self.talks.select_related('submission', 'submission__event', 'room').all()\n            if talk.is_visible\n        )\n        old_slots = set(\n            talk\n            for talk in self.previous_schedule.talks.select_related('submission', 'submission__event', 'room').all()\n            if talk.is_visible\n        )\n\n        new_submissions = set(talk.submission for talk in new_slots)\n        old_submissions = set(talk.submission for talk in old_slots)\n\n        new_slot_by_submission = {talk.submission: talk for talk in new_slots}\n        old_slot_by_submission = {talk.submission: talk for talk in old_slots}\n\n        result['new_talks'] = [new_slot_by_submission.get(s) for s in new_submissions - old_submissions]\n        result['canceled_talks'] = [old_slot_by_submission.get(s) for s in old_submissions - new_submissions]\n\n        for submission in (new_submissions & old_submissions):\n            old_slot = old_slot_by_submission.get(submission)\n            new_slot = new_slot_by_submission.get(submission)\n            if new_slot.room and not old_slot.room:\n                result['new_talks'].append(new_slot)\n            elif not new_slot.room and old_slot.room:\n                result['canceled_talks'].append(new_slot)\n            elif old_slot.start != new_slot.start or old_slot.room != new_slot.room:\n                if new_slot.room:\n                    result['moved_talks'].append({\n                        'submission': submission,\n                        'old_start': old_slot.start.astimezone(tz),\n                        'new_start': new_slot.start.astimezone(tz),\n                        'old_room': old_slot.room.name,\n                        'new_room': new_slot.room.name,\n                    })\n\n        result['count'] = len(result['new_talks']) + len(result['canceled_talks']) + len(result['moved_talks'])\n        return result\n\n    def notify_speakers(self):\n        tz = pytz.timezone(self.event.timezone)\n        speakers = defaultdict(lambda: {'create': [], 'update': []})\n        if self.changes['action'] == 'create':\n            speakers = {\n                speaker: {'create': self.talks.filter(submission__speakers=speaker), 'update': []}\n                for speaker in User.objects.filter(submissions__slots__schedule=self)\n            }\n        else:\n            if self.changes['count'] == len(self.changes['canceled_talks']):\n                return\n\n            for new_talk in self.changes['new_talks']:\n                for speaker in new_talk.submission.speakers.all():\n                    speakers[speaker]['create'].append(new_talk)\n            for moved_talk in self.changes['moved_talks']:\n                for speaker in moved_talk['submission'].speakers.all():\n                    speakers[speaker]['update'].append(moved_talk)\n        for speaker in speakers:\n            with override(speaker.locale), tzoverride(tz):\n                text = get_template('schedule/speaker_notification.txt').render(\n                    {'speaker': speaker, **speakers[speaker]}\n                )\n            QueuedMail.objects.create(\n                event=self.event,\n                to=speaker.email,\n                reply_to=self.event.email,\n                subject=_('[{event}] New schedule!').format(event=self.event.slug),\n                text=text\n            )\n\n    def __str__(self) -> str:\n        return str(self.version) or _(f'WIP Schedule for {self.event}')\n", "path": "src/pretalx/schedule/models/schedule.py"}], "after_files": [{"content": "from collections import defaultdict\nfrom contextlib import suppress\n\nimport pytz\nfrom django.db import models, transaction\nfrom django.template.loader import get_template\nfrom django.utils.functional import cached_property\nfrom django.utils.timezone import now, override as tzoverride\nfrom django.utils.translation import override, ugettext_lazy as _\n\nfrom pretalx.common.mixins import LogMixin\nfrom pretalx.mail.models import QueuedMail\nfrom pretalx.person.models import User\nfrom pretalx.submission.models import SubmissionStates\n\n\nclass Schedule(LogMixin, models.Model):\n    event = models.ForeignKey(\n        to='event.Event',\n        on_delete=models.PROTECT,\n        related_name='schedules',\n    )\n    version = models.CharField(\n        max_length=200,\n        null=True, blank=True,\n    )\n    published = models.DateTimeField(\n        null=True, blank=True\n    )\n\n    class Meta:\n        ordering = ('-published', )\n        unique_together = (('event', 'version'), )\n\n    @transaction.atomic\n    def freeze(self, name, user=None):\n        from pretalx.schedule.models import TalkSlot\n        if self.version:\n            raise Exception(f'Cannot freeze schedule version: already versioned as \"{self.version}\".')\n\n        self.version = name\n        self.published = now()\n        self.save(update_fields=['published', 'version'])\n        self.log_action('pretalx.schedule.release', person=user, orga=True)\n\n        wip_schedule = Schedule.objects.create(event=self.event)\n\n        # Set visibility\n        self.talks.filter(\n            start__isnull=False, submission__state=SubmissionStates.CONFIRMED, is_visible=False\n        ).update(is_visible=True)\n        self.talks.filter(is_visible=True).exclude(\n            start__isnull=False, submission__state=SubmissionStates.CONFIRMED\n        ).update(is_visible=False)\n\n        talks = []\n        for talk in self.talks.select_related('submission', 'room').all():\n            talks.append(talk.copy_to_schedule(wip_schedule, save=False))\n        TalkSlot.objects.bulk_create(talks)\n\n        self.notify_speakers()\n\n        with suppress(AttributeError):\n            del wip_schedule.event.wip_schedule\n\n        return self, wip_schedule\n\n    def unfreeze(self, user=None):\n        from pretalx.schedule.models import TalkSlot\n        if not self.version:\n            raise Exception('Cannot unfreeze schedule version: not released yet.')\n\n        # collect all talks, which have been added since this schedule (#72)\n        submission_ids = self.talks.all().values_list('submission_id', flat=True)\n        talks = self.event.wip_schedule.talks \\\n            .exclude(submission_id__in=submission_ids) \\\n            .union(self.talks.all())\n\n        wip_schedule = Schedule.objects.create(event=self.event)\n        new_talks = []\n        for talk in talks:\n            new_talks.append(talk.copy_to_schedule(wip_schedule, save=False))\n        TalkSlot.objects.bulk_create(new_talks)\n\n        self.event.wip_schedule.talks.all().delete()\n        self.event.wip_schedule.delete()\n\n        with suppress(AttributeError):\n            del wip_schedule.event.wip_schedule\n\n        return self, wip_schedule\n\n    @cached_property\n    def scheduled_talks(self):\n        return self.talks.filter(\n            room__isnull=False,\n            start__isnull=False,\n        )\n\n    @cached_property\n    def previous_schedule(self):\n        return self.event.schedules.filter(published__lt=self.published).order_by('-published').first()\n\n    @cached_property\n    def changes(self):\n        tz = pytz.timezone(self.event.timezone)\n        result = {\n            'count': 0,\n            'action': 'update',\n            'new_talks': [],\n            'canceled_talks': [],\n            'moved_talks': [],\n        }\n        if not self.previous_schedule:\n            result['action'] = 'create'\n            return result\n\n        new_slots = set(\n            talk\n            for talk in self.talks.select_related('submission', 'submission__event', 'room').all()\n            if talk.is_visible\n        )\n        old_slots = set(\n            talk\n            for talk in self.previous_schedule.talks.select_related('submission', 'submission__event', 'room').all()\n            if talk.is_visible\n        )\n\n        new_submissions = set(talk.submission for talk in new_slots)\n        old_submissions = set(talk.submission for talk in old_slots)\n\n        new_slot_by_submission = {talk.submission: talk for talk in new_slots}\n        old_slot_by_submission = {talk.submission: talk for talk in old_slots}\n\n        result['new_talks'] = [new_slot_by_submission.get(s) for s in new_submissions - old_submissions]\n        result['canceled_talks'] = [old_slot_by_submission.get(s) for s in old_submissions - new_submissions]\n\n        for submission in (new_submissions & old_submissions):\n            old_slot = old_slot_by_submission.get(submission)\n            new_slot = new_slot_by_submission.get(submission)\n            if new_slot.room and not old_slot.room:\n                result['new_talks'].append(new_slot)\n            elif not new_slot.room and old_slot.room:\n                result['canceled_talks'].append(new_slot)\n            elif old_slot.start != new_slot.start or old_slot.room != new_slot.room:\n                if new_slot.room:\n                    result['moved_talks'].append({\n                        'submission': submission,\n                        'old_start': old_slot.start.astimezone(tz),\n                        'new_start': new_slot.start.astimezone(tz),\n                        'old_room': old_slot.room.name,\n                        'new_room': new_slot.room.name,\n                    })\n\n        result['count'] = len(result['new_talks']) + len(result['canceled_talks']) + len(result['moved_talks'])\n        return result\n\n    def notify_speakers(self):\n        tz = pytz.timezone(self.event.timezone)\n        speakers = defaultdict(lambda: {'create': [], 'update': []})\n        if self.changes['action'] == 'create':\n            speakers = {\n                speaker: {'create': self.talks.filter(submission__speakers=speaker), 'update': []}\n                for speaker in User.objects.filter(submissions__slots__schedule=self)\n            }\n        else:\n            if self.changes['count'] == len(self.changes['canceled_talks']):\n                return\n\n            for new_talk in self.changes['new_talks']:\n                for speaker in new_talk.submission.speakers.all():\n                    speakers[speaker]['create'].append(new_talk)\n            for moved_talk in self.changes['moved_talks']:\n                for speaker in moved_talk['submission'].speakers.all():\n                    speakers[speaker]['update'].append(moved_talk)\n        for speaker in speakers:\n            with override(speaker.locale), tzoverride(tz):\n                text = get_template('schedule/speaker_notification.txt').render(\n                    {'speaker': speaker, **speakers[speaker]}\n                )\n            QueuedMail.objects.create(\n                event=self.event,\n                to=speaker.email,\n                reply_to=self.event.email,\n                subject=_('[{event}] New schedule!').format(event=self.event.slug),\n                text=text\n            )\n\n    def __str__(self) -> str:\n        return str(self.version) or _(f'WIP Schedule for {self.event}')\n", "path": "src/pretalx/schedule/models/schedule.py"}]}