We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
notification sent for modules in de/unpublished project
**URL:** (depending on follow status, so URL does not help here)
**user:** follower
**expected behaviour:** I should not get a notification for modules in unpublished projects
**behaviour:** If I follow an unpublished or depublished project (e.g. because a project had been published and after a participation was depublished or because the initiator followed in preview before publishing) I get a notification eg for start of participation. The Module has to be added to project.
**important screensize:**
**device & browser:** 
**Comment/Question:** it is an edge case but was topic in two support questions

Screenshot?


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `meinberlin/apps/notifications/emails.py`
Content:
```
1 from django.contrib import auth
2 
3 from meinberlin.apps.contrib.emails import Email
4 
5 User = auth.get_user_model()
6 
7 
8 def _exclude_actor(receivers, actor):
9     if not actor:
10         return receivers
11 
12     if hasattr(receivers, "exclude"):
13         return receivers.exclude(id=actor.id)
14 
15     return [receiver for receiver in receivers if not receiver == actor]
16 
17 
18 def _exclude_moderators(receivers, action):
19     if hasattr(action, "project"):
20         moderator_ids = action.project.moderators.values_list("id", flat=True)
21 
22         if hasattr(receivers, "exclude"):
23             return receivers.exclude(id__in=moderator_ids)
24 
25         return [user for user in receivers if user.id not in moderator_ids]
26 
27     return receivers
28 
29 
30 def _exclude_notifications_disabled(receivers):
31     if hasattr(receivers, "filter"):
32         return receivers.filter(get_notifications=True)
33 
34     return [user for user in receivers if user.get_notifications]
35 
36 
37 class NotifyCreatorEmail(Email):
38     template_name = "meinberlin_notifications/emails/notify_creator"
39 
40     def get_receivers(self):
41         action = self.object
42         if hasattr(action.target, "creator"):
43             receivers = [action.target.creator]
44             receivers = _exclude_notifications_disabled(receivers)
45             receivers = _exclude_actor(receivers, action.actor)
46             receivers = _exclude_moderators(receivers, action)
47             return receivers
48         return []
49 
50 
51 class NotifyCreatorOrContactOnModeratorFeedback(Email):
52     template_name = (
53         "meinberlin_notifications/emails/notify_creator_on_moderator_feedback"
54     )
55 
56     def get_receivers(self):
57         if hasattr(self.object, "contact_email"):
58             #  send to contact
59             receivers = [self.object.contact_email]
60         else:
61             #  send to creator
62             receivers = [self.object.creator]
63             receivers = _exclude_notifications_disabled(receivers)
64         return receivers
65 
66     def get_context(self):
67         context = super().get_context()
68         context["object"] = self.object
69         if not hasattr(self.object, "contact_email"):
70             #  send to creator
71             context["send_to_creator"] = True
72         return context
73 
74 
75 class NotifyModeratorsEmail(Email):
76     template_name = "meinberlin_notifications/emails/notify_moderator"
77 
78     def get_receivers(self):
79         action = self.object
80         receivers = action.project.moderators.all()
81         receivers = _exclude_actor(receivers, action.actor)
82         receivers = _exclude_notifications_disabled(receivers)
83         return receivers
84 
85 
86 class NotifyInitiatorsOnProjectCreatedEmail(Email):
87     template_name = "meinberlin_notifications/emails/notify_initiators_project_created"
88 
89     def get_receivers(self):
90         project = self.object
91         creator = User.objects.get(pk=self.kwargs["creator_pk"])
92         receivers = project.organisation.initiators.all()
93         receivers = _exclude_actor(receivers, creator)
94         receivers = _exclude_notifications_disabled(receivers)
95         return receivers
96 
97     def get_context(self):
98         context = super().get_context()
99         creator = User.objects.get(pk=self.kwargs["creator_pk"])
100         context["creator"] = creator
101         context["project"] = self.object
102         return context
103 
104 
105 class NotifyFollowersOnPhaseStartedEmail(Email):
106     template_name = "meinberlin_notifications/emails" "/notify_followers_phase_started"
107 
108     def get_receivers(self):
109         action = self.object
110         receivers = User.objects.filter(
111             follow__project=action.project,
112             follow__enabled=True,
113         )
114         receivers = _exclude_notifications_disabled(receivers)
115         return receivers
116 
117 
118 class NotifyFollowersOnPhaseIsOverSoonEmail(Email):
119     template_name = (
120         "meinberlin_notifications/emails" "/notify_followers_phase_over_soon"
121     )
122 
123     def get_receivers(self):
124         action = self.object
125         receivers = User.objects.filter(
126             follow__project=action.project,
127             follow__enabled=True,
128         )
129         receivers = _exclude_notifications_disabled(receivers)
130         return receivers
131 
132 
133 class NotifyFollowersOnUpcommingEventEmail(Email):
134     template_name = (
135         "meinberlin_notifications/emails" "/notify_followers_event_upcomming"
136     )
137 
138     def get_receivers(self):
139         action = self.object
140         receivers = User.objects.filter(
141             follow__project=action.project,
142             follow__enabled=True,
143         )
144         receivers = _exclude_notifications_disabled(receivers)
145         return receivers
146
```

Path: `meinberlin/apps/notifications/signals.py`
Content:
```
1 from django.contrib.auth import get_user_model
2 from django.db.models import signals
3 from django.dispatch import receiver
4 
5 from adhocracy4.actions.models import Action
6 from adhocracy4.actions.verbs import Verbs
7 from adhocracy4.dashboard import signals as dashboard_signals
8 from adhocracy4.follows.models import Follow
9 from adhocracy4.projects.models import Project
10 
11 from . import emails
12 
13 User = get_user_model()
14 
15 
16 @receiver(signals.post_save, sender=Action)
17 def send_notifications(instance, created, **kwargs):
18     action = instance
19     verb = Verbs(action.verb)
20 
21     if action.type in ("item", "comment") and verb in (Verbs.CREATE, Verbs.ADD):
22         emails.NotifyCreatorEmail.send(action)
23 
24         if action.project:
25             emails.NotifyModeratorsEmail.send(action)
26 
27     elif action.type == "phase" and action.project.project_type == "a4projects.Project":
28         if verb == Verbs.START:
29             emails.NotifyFollowersOnPhaseStartedEmail.send(action)
30         elif verb == Verbs.SCHEDULE:
31             emails.NotifyFollowersOnPhaseIsOverSoonEmail.send(action)
32 
33     elif action.type == "offlineevent" and verb == Verbs.START:
34         emails.NotifyFollowersOnUpcommingEventEmail.send(action)
35 
36 
37 @receiver(dashboard_signals.project_created)
38 def send_project_created_notifications(**kwargs):
39     project = kwargs.get("project")
40     creator = kwargs.get("user")
41     emails.NotifyInitiatorsOnProjectCreatedEmail.send(project, creator_pk=creator.pk)
42 
43 
44 @receiver(signals.m2m_changed, sender=Project.moderators.through)
45 def autofollow_project_moderators(instance, action, pk_set, reverse, **kwargs):
46     if action == "post_add":
47         autofollow_project(instance, pk_set, reverse)
48 
49 
50 def autofollow_project(instance, pk_set, reverse):
51     if not reverse:
52         project = instance
53         users_pks = pk_set
54 
55         for user_pk in users_pks:
56             Follow.objects.update_or_create(
57                 project=project, creator_id=user_pk, defaults={"enabled": True}
58             )
59     else:
60         user = instance
61         project_pks = pk_set
62 
63         for project_pk in project_pks:
64             Follow.objects.update_or_create(
65                 project_id=project_pk, creator=user, defaults={"enabled": True}
66             )
67
```

Path: `meinberlin/apps/offlineevents/management/commands/create_offlineevent_system_actions.py`
Content:
```
1 from datetime import timedelta
2 
3 from django.conf import settings
4 from django.contrib.contenttypes.models import ContentType
5 from django.core.management.base import BaseCommand
6 
7 from adhocracy4.actions.models import Action
8 from adhocracy4.actions.verbs import Verbs
9 from meinberlin.apps.offlineevents.models import OfflineEvent
10 
11 
12 class Command(BaseCommand):
13     help = "Create offlineevent system actions."
14 
15     def __init__(self):
16         if hasattr(settings, "ACTIONS_OFFLINE_EVENT_STARTING_HOURS"):
17             self.event_starting_hours = settings.ACTIONS_OFFLINE_EVENT_STARTING_HOURS
18         else:
19             self.event_starting_hours = 72
20 
21     def handle(self, *args, **options):
22         self._event_starting()
23 
24     def _event_starting(self):
25         event_ct = ContentType.objects.get_for_model(OfflineEvent)
26 
27         events = OfflineEvent.objects.starts_within(hours=self.event_starting_hours)
28         for event in events:
29             existing_action = Action.objects.filter(
30                 project=event.project,
31                 verb=Verbs.START.value,
32                 obj_content_type=event_ct,
33                 obj_object_id=event.id,
34             ).first()
35 
36             # If the event date has been modified and moved more than
37             # event_starting_hours ahead, schedule a new action
38             if (
39                 not existing_action
40                 or (
41                     existing_action.timestamp
42                     + timedelta(hours=self.event_starting_hours)
43                 )
44                 < event.date
45             ):
46                 Action.objects.create(
47                     project=event.project,
48                     verb=Verbs.START.value,
49                     obj=event,
50                     timestamp=event.date,
51                 )
52             elif existing_action.timestamp != event.date:
53                 existing_action.timestamp = event.date
54                 existing_action.save()
55
```
--- 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/meinberlin/apps/notifications/emails.py b/meinberlin/apps/notifications/emails.py
--- a/meinberlin/apps/notifications/emails.py
+++ b/meinberlin/apps/notifications/emails.py
@@ -130,10 +130,8 @@
         return receivers
 
 
-class NotifyFollowersOnUpcommingEventEmail(Email):
-    template_name = (
-        "meinberlin_notifications/emails" "/notify_followers_event_upcomming"
-    )
+class NotifyFollowersOnUpcomingEventEmail(Email):
+    template_name = "meinberlin_notifications/emails/notify_followers_event_upcoming"
 
     def get_receivers(self):
         action = self.object
diff --git a/meinberlin/apps/notifications/signals.py b/meinberlin/apps/notifications/signals.py
--- a/meinberlin/apps/notifications/signals.py
+++ b/meinberlin/apps/notifications/signals.py
@@ -31,7 +31,7 @@
             emails.NotifyFollowersOnPhaseIsOverSoonEmail.send(action)
 
     elif action.type == "offlineevent" and verb == Verbs.START:
-        emails.NotifyFollowersOnUpcommingEventEmail.send(action)
+        emails.NotifyFollowersOnUpcomingEventEmail.send(action)
 
 
 @receiver(dashboard_signals.project_created)
diff --git a/meinberlin/apps/offlineevents/management/commands/create_offlineevent_system_actions.py b/meinberlin/apps/offlineevents/management/commands/create_offlineevent_system_actions.py
--- a/meinberlin/apps/offlineevents/management/commands/create_offlineevent_system_actions.py
+++ b/meinberlin/apps/offlineevents/management/commands/create_offlineevent_system_actions.py
@@ -24,7 +24,9 @@
     def _event_starting(self):
         event_ct = ContentType.objects.get_for_model(OfflineEvent)
 
-        events = OfflineEvent.objects.starts_within(hours=self.event_starting_hours)
+        events = OfflineEvent.objects.starts_within(
+            hours=self.event_starting_hours
+        ).exclude(project__is_draft=True)
         for event in events:
             existing_action = Action.objects.filter(
                 project=event.project,

{"golden_diff": "diff --git a/meinberlin/apps/notifications/emails.py b/meinberlin/apps/notifications/emails.py\n--- a/meinberlin/apps/notifications/emails.py\n+++ b/meinberlin/apps/notifications/emails.py\n@@ -130,10 +130,8 @@\n         return receivers\n \n \n-class NotifyFollowersOnUpcommingEventEmail(Email):\n-    template_name = (\n-        \"meinberlin_notifications/emails\" \"/notify_followers_event_upcomming\"\n-    )\n+class NotifyFollowersOnUpcomingEventEmail(Email):\n+    template_name = \"meinberlin_notifications/emails/notify_followers_event_upcoming\"\n \n     def get_receivers(self):\n         action = self.object\ndiff --git a/meinberlin/apps/notifications/signals.py b/meinberlin/apps/notifications/signals.py\n--- a/meinberlin/apps/notifications/signals.py\n+++ b/meinberlin/apps/notifications/signals.py\n@@ -31,7 +31,7 @@\n             emails.NotifyFollowersOnPhaseIsOverSoonEmail.send(action)\n \n     elif action.type == \"offlineevent\" and verb == Verbs.START:\n-        emails.NotifyFollowersOnUpcommingEventEmail.send(action)\n+        emails.NotifyFollowersOnUpcomingEventEmail.send(action)\n \n \n @receiver(dashboard_signals.project_created)\ndiff --git a/meinberlin/apps/offlineevents/management/commands/create_offlineevent_system_actions.py b/meinberlin/apps/offlineevents/management/commands/create_offlineevent_system_actions.py\n--- a/meinberlin/apps/offlineevents/management/commands/create_offlineevent_system_actions.py\n+++ b/meinberlin/apps/offlineevents/management/commands/create_offlineevent_system_actions.py\n@@ -24,7 +24,9 @@\n     def _event_starting(self):\n         event_ct = ContentType.objects.get_for_model(OfflineEvent)\n \n-        events = OfflineEvent.objects.starts_within(hours=self.event_starting_hours)\n+        events = OfflineEvent.objects.starts_within(\n+            hours=self.event_starting_hours\n+        ).exclude(project__is_draft=True)\n         for event in events:\n             existing_action = Action.objects.filter(\n                 project=event.project,\n", "issue": "notification sent for modules in de/unpublished project\n**URL:** (depending on follow status, so URL does not help here)\r\n**user:** follower\r\n**expected behaviour:** I should not get a notification for modules in unpublished projects\r\n**behaviour:** If I follow an unpublished or depublished project (e.g. because a project had been published and after a participation was depublished or because the initiator followed in preview before publishing) I get a notification eg for start of participation. The Module has to be added to project.\r\n**important screensize:**\r\n**device & browser:** \r\n**Comment/Question:** it is an edge case but was topic in two support questions\r\n\r\nScreenshot?\r\n\n", "before_files": [{"content": "from django.contrib import auth\n\nfrom meinberlin.apps.contrib.emails import Email\n\nUser = auth.get_user_model()\n\n\ndef _exclude_actor(receivers, actor):\n    if not actor:\n        return receivers\n\n    if hasattr(receivers, \"exclude\"):\n        return receivers.exclude(id=actor.id)\n\n    return [receiver for receiver in receivers if not receiver == actor]\n\n\ndef _exclude_moderators(receivers, action):\n    if hasattr(action, \"project\"):\n        moderator_ids = action.project.moderators.values_list(\"id\", flat=True)\n\n        if hasattr(receivers, \"exclude\"):\n            return receivers.exclude(id__in=moderator_ids)\n\n        return [user for user in receivers if user.id not in moderator_ids]\n\n    return receivers\n\n\ndef _exclude_notifications_disabled(receivers):\n    if hasattr(receivers, \"filter\"):\n        return receivers.filter(get_notifications=True)\n\n    return [user for user in receivers if user.get_notifications]\n\n\nclass NotifyCreatorEmail(Email):\n    template_name = \"meinberlin_notifications/emails/notify_creator\"\n\n    def get_receivers(self):\n        action = self.object\n        if hasattr(action.target, \"creator\"):\n            receivers = [action.target.creator]\n            receivers = _exclude_notifications_disabled(receivers)\n            receivers = _exclude_actor(receivers, action.actor)\n            receivers = _exclude_moderators(receivers, action)\n            return receivers\n        return []\n\n\nclass NotifyCreatorOrContactOnModeratorFeedback(Email):\n    template_name = (\n        \"meinberlin_notifications/emails/notify_creator_on_moderator_feedback\"\n    )\n\n    def get_receivers(self):\n        if hasattr(self.object, \"contact_email\"):\n            #  send to contact\n            receivers = [self.object.contact_email]\n        else:\n            #  send to creator\n            receivers = [self.object.creator]\n            receivers = _exclude_notifications_disabled(receivers)\n        return receivers\n\n    def get_context(self):\n        context = super().get_context()\n        context[\"object\"] = self.object\n        if not hasattr(self.object, \"contact_email\"):\n            #  send to creator\n            context[\"send_to_creator\"] = True\n        return context\n\n\nclass NotifyModeratorsEmail(Email):\n    template_name = \"meinberlin_notifications/emails/notify_moderator\"\n\n    def get_receivers(self):\n        action = self.object\n        receivers = action.project.moderators.all()\n        receivers = _exclude_actor(receivers, action.actor)\n        receivers = _exclude_notifications_disabled(receivers)\n        return receivers\n\n\nclass NotifyInitiatorsOnProjectCreatedEmail(Email):\n    template_name = \"meinberlin_notifications/emails/notify_initiators_project_created\"\n\n    def get_receivers(self):\n        project = self.object\n        creator = User.objects.get(pk=self.kwargs[\"creator_pk\"])\n        receivers = project.organisation.initiators.all()\n        receivers = _exclude_actor(receivers, creator)\n        receivers = _exclude_notifications_disabled(receivers)\n        return receivers\n\n    def get_context(self):\n        context = super().get_context()\n        creator = User.objects.get(pk=self.kwargs[\"creator_pk\"])\n        context[\"creator\"] = creator\n        context[\"project\"] = self.object\n        return context\n\n\nclass NotifyFollowersOnPhaseStartedEmail(Email):\n    template_name = \"meinberlin_notifications/emails\" \"/notify_followers_phase_started\"\n\n    def get_receivers(self):\n        action = self.object\n        receivers = User.objects.filter(\n            follow__project=action.project,\n            follow__enabled=True,\n        )\n        receivers = _exclude_notifications_disabled(receivers)\n        return receivers\n\n\nclass NotifyFollowersOnPhaseIsOverSoonEmail(Email):\n    template_name = (\n        \"meinberlin_notifications/emails\" \"/notify_followers_phase_over_soon\"\n    )\n\n    def get_receivers(self):\n        action = self.object\n        receivers = User.objects.filter(\n            follow__project=action.project,\n            follow__enabled=True,\n        )\n        receivers = _exclude_notifications_disabled(receivers)\n        return receivers\n\n\nclass NotifyFollowersOnUpcommingEventEmail(Email):\n    template_name = (\n        \"meinberlin_notifications/emails\" \"/notify_followers_event_upcomming\"\n    )\n\n    def get_receivers(self):\n        action = self.object\n        receivers = User.objects.filter(\n            follow__project=action.project,\n            follow__enabled=True,\n        )\n        receivers = _exclude_notifications_disabled(receivers)\n        return receivers\n", "path": "meinberlin/apps/notifications/emails.py"}, {"content": "from django.contrib.auth import get_user_model\nfrom django.db.models import signals\nfrom django.dispatch import receiver\n\nfrom adhocracy4.actions.models import Action\nfrom adhocracy4.actions.verbs import Verbs\nfrom adhocracy4.dashboard import signals as dashboard_signals\nfrom adhocracy4.follows.models import Follow\nfrom adhocracy4.projects.models import Project\n\nfrom . import emails\n\nUser = get_user_model()\n\n\n@receiver(signals.post_save, sender=Action)\ndef send_notifications(instance, created, **kwargs):\n    action = instance\n    verb = Verbs(action.verb)\n\n    if action.type in (\"item\", \"comment\") and verb in (Verbs.CREATE, Verbs.ADD):\n        emails.NotifyCreatorEmail.send(action)\n\n        if action.project:\n            emails.NotifyModeratorsEmail.send(action)\n\n    elif action.type == \"phase\" and action.project.project_type == \"a4projects.Project\":\n        if verb == Verbs.START:\n            emails.NotifyFollowersOnPhaseStartedEmail.send(action)\n        elif verb == Verbs.SCHEDULE:\n            emails.NotifyFollowersOnPhaseIsOverSoonEmail.send(action)\n\n    elif action.type == \"offlineevent\" and verb == Verbs.START:\n        emails.NotifyFollowersOnUpcommingEventEmail.send(action)\n\n\n@receiver(dashboard_signals.project_created)\ndef send_project_created_notifications(**kwargs):\n    project = kwargs.get(\"project\")\n    creator = kwargs.get(\"user\")\n    emails.NotifyInitiatorsOnProjectCreatedEmail.send(project, creator_pk=creator.pk)\n\n\n@receiver(signals.m2m_changed, sender=Project.moderators.through)\ndef autofollow_project_moderators(instance, action, pk_set, reverse, **kwargs):\n    if action == \"post_add\":\n        autofollow_project(instance, pk_set, reverse)\n\n\ndef autofollow_project(instance, pk_set, reverse):\n    if not reverse:\n        project = instance\n        users_pks = pk_set\n\n        for user_pk in users_pks:\n            Follow.objects.update_or_create(\n                project=project, creator_id=user_pk, defaults={\"enabled\": True}\n            )\n    else:\n        user = instance\n        project_pks = pk_set\n\n        for project_pk in project_pks:\n            Follow.objects.update_or_create(\n                project_id=project_pk, creator=user, defaults={\"enabled\": True}\n            )\n", "path": "meinberlin/apps/notifications/signals.py"}, {"content": "from datetime import timedelta\n\nfrom django.conf import settings\nfrom django.contrib.contenttypes.models import ContentType\nfrom django.core.management.base import BaseCommand\n\nfrom adhocracy4.actions.models import Action\nfrom adhocracy4.actions.verbs import Verbs\nfrom meinberlin.apps.offlineevents.models import OfflineEvent\n\n\nclass Command(BaseCommand):\n    help = \"Create offlineevent system actions.\"\n\n    def __init__(self):\n        if hasattr(settings, \"ACTIONS_OFFLINE_EVENT_STARTING_HOURS\"):\n            self.event_starting_hours = settings.ACTIONS_OFFLINE_EVENT_STARTING_HOURS\n        else:\n            self.event_starting_hours = 72\n\n    def handle(self, *args, **options):\n        self._event_starting()\n\n    def _event_starting(self):\n        event_ct = ContentType.objects.get_for_model(OfflineEvent)\n\n        events = OfflineEvent.objects.starts_within(hours=self.event_starting_hours)\n        for event in events:\n            existing_action = Action.objects.filter(\n                project=event.project,\n                verb=Verbs.START.value,\n                obj_content_type=event_ct,\n                obj_object_id=event.id,\n            ).first()\n\n            # If the event date has been modified and moved more than\n            # event_starting_hours ahead, schedule a new action\n            if (\n                not existing_action\n                or (\n                    existing_action.timestamp\n                    + timedelta(hours=self.event_starting_hours)\n                )\n                < event.date\n            ):\n                Action.objects.create(\n                    project=event.project,\n                    verb=Verbs.START.value,\n                    obj=event,\n                    timestamp=event.date,\n                )\n            elif existing_action.timestamp != event.date:\n                existing_action.timestamp = event.date\n                existing_action.save()\n", "path": "meinberlin/apps/offlineevents/management/commands/create_offlineevent_system_actions.py"}], "after_files": [{"content": "from django.contrib import auth\n\nfrom meinberlin.apps.contrib.emails import Email\n\nUser = auth.get_user_model()\n\n\ndef _exclude_actor(receivers, actor):\n    if not actor:\n        return receivers\n\n    if hasattr(receivers, \"exclude\"):\n        return receivers.exclude(id=actor.id)\n\n    return [receiver for receiver in receivers if not receiver == actor]\n\n\ndef _exclude_moderators(receivers, action):\n    if hasattr(action, \"project\"):\n        moderator_ids = action.project.moderators.values_list(\"id\", flat=True)\n\n        if hasattr(receivers, \"exclude\"):\n            return receivers.exclude(id__in=moderator_ids)\n\n        return [user for user in receivers if user.id not in moderator_ids]\n\n    return receivers\n\n\ndef _exclude_notifications_disabled(receivers):\n    if hasattr(receivers, \"filter\"):\n        return receivers.filter(get_notifications=True)\n\n    return [user for user in receivers if user.get_notifications]\n\n\nclass NotifyCreatorEmail(Email):\n    template_name = \"meinberlin_notifications/emails/notify_creator\"\n\n    def get_receivers(self):\n        action = self.object\n        if hasattr(action.target, \"creator\"):\n            receivers = [action.target.creator]\n            receivers = _exclude_notifications_disabled(receivers)\n            receivers = _exclude_actor(receivers, action.actor)\n            receivers = _exclude_moderators(receivers, action)\n            return receivers\n        return []\n\n\nclass NotifyCreatorOrContactOnModeratorFeedback(Email):\n    template_name = (\n        \"meinberlin_notifications/emails/notify_creator_on_moderator_feedback\"\n    )\n\n    def get_receivers(self):\n        if hasattr(self.object, \"contact_email\"):\n            #  send to contact\n            receivers = [self.object.contact_email]\n        else:\n            #  send to creator\n            receivers = [self.object.creator]\n            receivers = _exclude_notifications_disabled(receivers)\n        return receivers\n\n    def get_context(self):\n        context = super().get_context()\n        context[\"object\"] = self.object\n        if not hasattr(self.object, \"contact_email\"):\n            #  send to creator\n            context[\"send_to_creator\"] = True\n        return context\n\n\nclass NotifyModeratorsEmail(Email):\n    template_name = \"meinberlin_notifications/emails/notify_moderator\"\n\n    def get_receivers(self):\n        action = self.object\n        receivers = action.project.moderators.all()\n        receivers = _exclude_actor(receivers, action.actor)\n        receivers = _exclude_notifications_disabled(receivers)\n        return receivers\n\n\nclass NotifyInitiatorsOnProjectCreatedEmail(Email):\n    template_name = \"meinberlin_notifications/emails/notify_initiators_project_created\"\n\n    def get_receivers(self):\n        project = self.object\n        creator = User.objects.get(pk=self.kwargs[\"creator_pk\"])\n        receivers = project.organisation.initiators.all()\n        receivers = _exclude_actor(receivers, creator)\n        receivers = _exclude_notifications_disabled(receivers)\n        return receivers\n\n    def get_context(self):\n        context = super().get_context()\n        creator = User.objects.get(pk=self.kwargs[\"creator_pk\"])\n        context[\"creator\"] = creator\n        context[\"project\"] = self.object\n        return context\n\n\nclass NotifyFollowersOnPhaseStartedEmail(Email):\n    template_name = \"meinberlin_notifications/emails\" \"/notify_followers_phase_started\"\n\n    def get_receivers(self):\n        action = self.object\n        receivers = User.objects.filter(\n            follow__project=action.project,\n            follow__enabled=True,\n        )\n        receivers = _exclude_notifications_disabled(receivers)\n        return receivers\n\n\nclass NotifyFollowersOnPhaseIsOverSoonEmail(Email):\n    template_name = (\n        \"meinberlin_notifications/emails\" \"/notify_followers_phase_over_soon\"\n    )\n\n    def get_receivers(self):\n        action = self.object\n        receivers = User.objects.filter(\n            follow__project=action.project,\n            follow__enabled=True,\n        )\n        receivers = _exclude_notifications_disabled(receivers)\n        return receivers\n\n\nclass NotifyFollowersOnUpcomingEventEmail(Email):\n    template_name = \"meinberlin_notifications/emails/notify_followers_event_upcoming\"\n\n    def get_receivers(self):\n        action = self.object\n        receivers = User.objects.filter(\n            follow__project=action.project,\n            follow__enabled=True,\n        )\n        receivers = _exclude_notifications_disabled(receivers)\n        return receivers\n", "path": "meinberlin/apps/notifications/emails.py"}, {"content": "from django.contrib.auth import get_user_model\nfrom django.db.models import signals\nfrom django.dispatch import receiver\n\nfrom adhocracy4.actions.models import Action\nfrom adhocracy4.actions.verbs import Verbs\nfrom adhocracy4.dashboard import signals as dashboard_signals\nfrom adhocracy4.follows.models import Follow\nfrom adhocracy4.projects.models import Project\n\nfrom . import emails\n\nUser = get_user_model()\n\n\n@receiver(signals.post_save, sender=Action)\ndef send_notifications(instance, created, **kwargs):\n    action = instance\n    verb = Verbs(action.verb)\n\n    if action.type in (\"item\", \"comment\") and verb in (Verbs.CREATE, Verbs.ADD):\n        emails.NotifyCreatorEmail.send(action)\n\n        if action.project:\n            emails.NotifyModeratorsEmail.send(action)\n\n    elif action.type == \"phase\" and action.project.project_type == \"a4projects.Project\":\n        if verb == Verbs.START:\n            emails.NotifyFollowersOnPhaseStartedEmail.send(action)\n        elif verb == Verbs.SCHEDULE:\n            emails.NotifyFollowersOnPhaseIsOverSoonEmail.send(action)\n\n    elif action.type == \"offlineevent\" and verb == Verbs.START:\n        emails.NotifyFollowersOnUpcomingEventEmail.send(action)\n\n\n@receiver(dashboard_signals.project_created)\ndef send_project_created_notifications(**kwargs):\n    project = kwargs.get(\"project\")\n    creator = kwargs.get(\"user\")\n    emails.NotifyInitiatorsOnProjectCreatedEmail.send(project, creator_pk=creator.pk)\n\n\n@receiver(signals.m2m_changed, sender=Project.moderators.through)\ndef autofollow_project_moderators(instance, action, pk_set, reverse, **kwargs):\n    if action == \"post_add\":\n        autofollow_project(instance, pk_set, reverse)\n\n\ndef autofollow_project(instance, pk_set, reverse):\n    if not reverse:\n        project = instance\n        users_pks = pk_set\n\n        for user_pk in users_pks:\n            Follow.objects.update_or_create(\n                project=project, creator_id=user_pk, defaults={\"enabled\": True}\n            )\n    else:\n        user = instance\n        project_pks = pk_set\n\n        for project_pk in project_pks:\n            Follow.objects.update_or_create(\n                project_id=project_pk, creator=user, defaults={\"enabled\": True}\n            )\n", "path": "meinberlin/apps/notifications/signals.py"}, {"content": "from datetime import timedelta\n\nfrom django.conf import settings\nfrom django.contrib.contenttypes.models import ContentType\nfrom django.core.management.base import BaseCommand\n\nfrom adhocracy4.actions.models import Action\nfrom adhocracy4.actions.verbs import Verbs\nfrom meinberlin.apps.offlineevents.models import OfflineEvent\n\n\nclass Command(BaseCommand):\n    help = \"Create offlineevent system actions.\"\n\n    def __init__(self):\n        if hasattr(settings, \"ACTIONS_OFFLINE_EVENT_STARTING_HOURS\"):\n            self.event_starting_hours = settings.ACTIONS_OFFLINE_EVENT_STARTING_HOURS\n        else:\n            self.event_starting_hours = 72\n\n    def handle(self, *args, **options):\n        self._event_starting()\n\n    def _event_starting(self):\n        event_ct = ContentType.objects.get_for_model(OfflineEvent)\n\n        events = OfflineEvent.objects.starts_within(\n            hours=self.event_starting_hours\n        ).exclude(project__is_draft=True)\n        for event in events:\n            existing_action = Action.objects.filter(\n                project=event.project,\n                verb=Verbs.START.value,\n                obj_content_type=event_ct,\n                obj_object_id=event.id,\n            ).first()\n\n            # If the event date has been modified and moved more than\n            # event_starting_hours ahead, schedule a new action\n            if (\n                not existing_action\n                or (\n                    existing_action.timestamp\n                    + timedelta(hours=self.event_starting_hours)\n                )\n                < event.date\n            ):\n                Action.objects.create(\n                    project=event.project,\n                    verb=Verbs.START.value,\n                    obj=event,\n                    timestamp=event.date,\n                )\n            elif existing_action.timestamp != event.date:\n                existing_action.timestamp = event.date\n                existing_action.save()\n", "path": "meinberlin/apps/offlineevents/management/commands/create_offlineevent_system_actions.py"}]}