mirror of
https://github.com/pretix/pretix.git
synced 2025-12-09 00:42:28 +00:00
Compare commits
1 Commits
dependabot
...
notificati
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
82e4c331fc |
@@ -22,6 +22,7 @@
|
|||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.db.models import Exists, OuterRef, Q
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
@@ -111,6 +112,24 @@ class OAuthRefreshToken(AbstractRefreshToken):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class WebHookQuerySet(models.QuerySet):
|
||||||
|
def for_notification(self, action_type, organizer, event):
|
||||||
|
event_listener = WebHookEventListener.objects.filter(
|
||||||
|
webhook=OuterRef('pk'),
|
||||||
|
action_type=action_type
|
||||||
|
)
|
||||||
|
webhooks = WebHook.objects.annotate(has_el=Exists(event_listener)).filter(
|
||||||
|
organizer=organizer,
|
||||||
|
has_el=True,
|
||||||
|
enabled=True
|
||||||
|
)
|
||||||
|
if event:
|
||||||
|
webhooks = webhooks.filter(
|
||||||
|
Q(all_events=True) | Q(limit_events__pk=event.pk if not isinstance(event, int) else event)
|
||||||
|
)
|
||||||
|
return webhooks
|
||||||
|
|
||||||
|
|
||||||
class WebHook(models.Model):
|
class WebHook(models.Model):
|
||||||
organizer = models.ForeignKey('pretixbase.Organizer', on_delete=models.CASCADE, related_name='webhooks')
|
organizer = models.ForeignKey('pretixbase.Organizer', on_delete=models.CASCADE, related_name='webhooks')
|
||||||
enabled = models.BooleanField(default=True, verbose_name=_("Enable webhook"))
|
enabled = models.BooleanField(default=True, verbose_name=_("Enable webhook"))
|
||||||
@@ -119,6 +138,8 @@ class WebHook(models.Model):
|
|||||||
limit_events = models.ManyToManyField('pretixbase.Event', verbose_name=_("Limit to events"), blank=True)
|
limit_events = models.ManyToManyField('pretixbase.Event', verbose_name=_("Limit to events"), blank=True)
|
||||||
comment = models.CharField(verbose_name=_("Comment"), max_length=255, null=True, blank=True)
|
comment = models.CharField(verbose_name=_("Comment"), max_length=255, null=True, blank=True)
|
||||||
|
|
||||||
|
objects = models.Manager.from_queryset(WebHookQuerySet)()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ('id',)
|
ordering = ('id',)
|
||||||
|
|
||||||
|
|||||||
@@ -27,16 +27,13 @@ from datetime import timedelta
|
|||||||
|
|
||||||
import requests
|
import requests
|
||||||
from django.db import DatabaseError, connection, transaction
|
from django.db import DatabaseError, connection, transaction
|
||||||
from django.db.models import Exists, OuterRef, Q
|
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
from django.utils.translation import gettext_lazy as _, pgettext_lazy
|
from django.utils.translation import gettext_lazy as _, pgettext_lazy
|
||||||
from django_scopes import scope, scopes_disabled
|
from django_scopes import scope, scopes_disabled
|
||||||
from requests import RequestException
|
from requests import RequestException
|
||||||
|
|
||||||
from pretix.api.models import (
|
from pretix.api.models import WebHook, WebHookCall, WebHookCallRetry
|
||||||
WebHook, WebHookCall, WebHookCallRetry, WebHookEventListener,
|
|
||||||
)
|
|
||||||
from pretix.api.signals import register_webhook_events
|
from pretix.api.signals import register_webhook_events
|
||||||
from pretix.base.models import LogEntry
|
from pretix.base.models import LogEntry
|
||||||
from pretix.base.services.tasks import ProfiledTask, TransactionAwareTask
|
from pretix.base.services.tasks import ProfiledTask, TransactionAwareTask
|
||||||
@@ -454,20 +451,9 @@ def notify_webhooks(logentry_ids: list):
|
|||||||
_org = logentry.organizer
|
_org = logentry.organizer
|
||||||
_at = logentry.action_type
|
_at = logentry.action_type
|
||||||
|
|
||||||
# All webhooks that registered for this notification
|
webhooks = WebHook.objects.for_notification(
|
||||||
event_listener = WebHookEventListener.objects.filter(
|
notification_type.action_type, logentry.organizer, logentry.event_id
|
||||||
webhook=OuterRef('pk'),
|
|
||||||
action_type=notification_type.action_type
|
|
||||||
)
|
)
|
||||||
webhooks = WebHook.objects.annotate(has_el=Exists(event_listener)).filter(
|
|
||||||
organizer=logentry.organizer,
|
|
||||||
has_el=True,
|
|
||||||
enabled=True
|
|
||||||
)
|
|
||||||
if logentry.event_id:
|
|
||||||
webhooks = webhooks.filter(
|
|
||||||
Q(all_events=True) | Q(limit_events__pk=logentry.event_id)
|
|
||||||
)
|
|
||||||
|
|
||||||
for wh in webhooks:
|
for wh in webhooks:
|
||||||
send_webhook.apply_async(args=(logentry.id, notification_type.action_type, wh.pk))
|
send_webhook.apply_async(args=(logentry.id, notification_type.action_type, wh.pk))
|
||||||
|
|||||||
@@ -38,8 +38,11 @@ import logging
|
|||||||
from django.contrib.contenttypes.fields import GenericForeignKey
|
from django.contrib.contenttypes.fields import GenericForeignKey
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.db import connections, models
|
from django.db import connections, models
|
||||||
|
from django.db.models import Q
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
|
|
||||||
|
from pretix.api.models import WebHook
|
||||||
|
|
||||||
|
|
||||||
class VisibleOnlyManager(models.Manager):
|
class VisibleOnlyManager(models.Manager):
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
@@ -181,12 +184,56 @@ class LogEntry(models.Model):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def bulk_postprocess(cls, objects):
|
def bulk_postprocess(cls, objects):
|
||||||
from pretix.api.webhooks import notify_webhooks
|
from pretix.api.webhooks import notify_webhooks
|
||||||
|
from pretix.base.models import NotificationSetting
|
||||||
|
|
||||||
from ..services.notifications import notify
|
from ..services.notifications import notify
|
||||||
|
|
||||||
to_notify = [o.id for o in objects if o.notification_type]
|
# Regular LogEntry.save() always kicks off notify and notify_webhooks tasks, regardless of whether a webhook
|
||||||
|
# listener exists. However, in bulk processing, it makes sense to check once and then only create the task if
|
||||||
|
# there is something to do.
|
||||||
|
_webhook_active_cache = {}
|
||||||
|
_notification_active_cache = {}
|
||||||
|
|
||||||
|
def _is_webhook_active(logentry):
|
||||||
|
nonlocal _webhook_active_cache
|
||||||
|
|
||||||
|
key = (logentry.action_type, logentry.organizer_id, logentry.event_id)
|
||||||
|
if key not in _webhook_active_cache:
|
||||||
|
notification_type = logentry.webhook_type
|
||||||
|
_webhook_active_cache[key] = notification_type and WebHook.objects.for_notification(
|
||||||
|
notification_type.action_type, logentry.organizer, logentry.event_id
|
||||||
|
).exists()
|
||||||
|
return _webhook_active_cache[key]
|
||||||
|
|
||||||
|
def _is_notification_active(logentry):
|
||||||
|
nonlocal _notification_active_cache
|
||||||
|
|
||||||
|
key = (logentry.action_type, logentry.organizer_id, logentry.event_id)
|
||||||
|
if key not in _notification_active_cache:
|
||||||
|
notification_type = logentry.notification_type
|
||||||
|
if notification_type and logentry.event:
|
||||||
|
# We only have event-related notifications right now
|
||||||
|
users = logentry.event.get_users_with_permission(
|
||||||
|
notification_type.required_permission
|
||||||
|
).filter(notifications_send=True, is_active=True)
|
||||||
|
|
||||||
|
_notification_active_cache[key] = NotificationSetting.objects.filter(
|
||||||
|
# This is not technically fully correct since it's returning True if a user has the
|
||||||
|
# notification enabled on the global level and then disabled on the per-event-level,
|
||||||
|
# but it's good enough as a first check to avoid useless celery tasks for bulk actions
|
||||||
|
Q(event_id=logentry.event_id) | Q(event__isnull=True),
|
||||||
|
action_type=notification_type.action_type,
|
||||||
|
user__pk__in=users.values_list('pk', flat=True),
|
||||||
|
enabled=True,
|
||||||
|
).exists()
|
||||||
|
|
||||||
|
else:
|
||||||
|
_webhook_active_cache[key] = False
|
||||||
|
return _notification_active_cache[key]
|
||||||
|
|
||||||
|
to_notify = [o.id for o in objects if _is_notification_active(o)]
|
||||||
if to_notify:
|
if to_notify:
|
||||||
notify.apply_async(args=(to_notify,))
|
notify.apply_async(args=(to_notify,))
|
||||||
to_wh = [o.id for o in objects if o.webhook_type]
|
to_wh = [o.id for o in objects if _is_webhook_active(o)]
|
||||||
if to_wh:
|
if to_wh:
|
||||||
notify_webhooks.apply_async(args=(to_wh,))
|
notify_webhooks.apply_async(args=(to_wh,))
|
||||||
|
|||||||
Reference in New Issue
Block a user