Exemple #1
0
    def notify(cls, user_or_email_, object_id=None, **filters):
        """Start notifying the given user or email address when this event
        occurs and meets the criteria given in `filters`.

        Return the created (or the existing matching) Watch so you can call
        activate() on it if you're so inclined.

        Implementations in subclasses may take different arguments; see the
        docstring of is_notifying().

        Send an activation email if an anonymous watch is created and
        settings.CONFIRM_ANONYMOUS_WATCHES = True. If the activation request
        fails, raise a ActivationRequestFailed exception.

        Calling notify() twice for an anonymous user will send the email
        each time.

        """
        # A test-for-existence-then-create race condition exists here, but it
        # doesn't matter: de-duplication on fire() and deletion of all matches
        # on stop_notifying() nullify its effects.
        try:
            # Pick 1 if >1 are returned:
            watch = cls._watches_belonging_to_user(
                user_or_email_,
                object_id=object_id,
                **filters)[0:1].get()
        except Watch.DoesNotExist:
            create_kwargs = {}
            if cls.content_type:
                create_kwargs['content_type'] = \
                    ContentType.objects.get_for_model(cls.content_type)
            create_kwargs['email' if isinstance(user_or_email_, basestring)
                          else 'user'] = user_or_email_
            secret = ''.join(random.choice(letters) for x in xrange(10))
            # Registered users don't need to confirm, but anonymous users do.
            is_active = ('user' in create_kwargs or
                          not settings.CONFIRM_ANONYMOUS_WATCHES)
            if object_id:
                create_kwargs['object_id'] = object_id
            watch = Watch.objects.create(
                secret=secret,
                is_active=is_active,
                event_type=cls.event_type,
                **create_kwargs)
            for k, v in filters.iteritems():
                WatchFilter.objects.create(watch=watch, name=k,
                                           value=hash_to_unsigned(v))
        # Send email for inactive watches.
        if not watch.is_active:
            email = watch.user.email if watch.user else watch.email
            message = cls._activation_email(watch, email)
            try:
                message.send()
            except SMTPException, e:
                watch.delete()
                raise ActivationRequestFailed(e.recipients)
Exemple #2
0
    def _watches_belonging_to_user(cls,
                                   user_or_email,
                                   object_id=None,
                                   **filters):
        """Return a QuerySet of watches having the given user or email, having
        (only) the given filters, and having the event_type and content_type
        attrs of the class.

        Matched Watches may be either confirmed and unconfirmed. They may
        include duplicates if the get-then-create race condition in notify()
        allowed them to be created.

        If you pass an email, it will be matched against only the email
        addresses of anonymous watches. At the moment, the only integration
        point planned between anonymous and registered watches is the claiming
        of anonymous watches of the same email address on user registration
        confirmation.

        If you pass the AnonymousUser, this will return an empty QuerySet.

        """
        # If we have trouble distinguishing subsets and such, we could store a
        # number_of_filters on the Watch.
        cls._validate_filters(filters)

        if isinstance(user_or_email, basestring):
            user_condition = Q(email=user_or_email)
        elif not user_or_email.is_anonymous():
            user_condition = Q(user=user_or_email)
        else:
            return Watch.objects.none()

        # Filter by stuff in the Watch row:
        watches = Watch.uncached.filter(
            user_condition,
            Q(content_type=ContentType.objects.get_for_model(cls.content_type))
            if cls.content_type else Q(),
            Q(object_id=object_id) if object_id else Q(),
            event_type=cls.event_type).extra(where=[
                '(SELECT count(*) FROM notifications_watchfilter WHERE '
                'notifications_watchfilter.watch_id='
                'notifications_watch.id)=%s'
            ],
                                             params=[len(filters)])
        # Optimization: If the subselect ends up being slow, store the number
        # of filters in each Watch row or try a GROUP BY.

        # Apply 1-to-many filters:
        for k, v in filters.iteritems():
            watches = watches.filter(filters__name=k,
                                     filters__value=hash_to_unsigned(v))

        return watches
Exemple #3
0
    def _watches_belonging_to_user(cls, user_or_email, object_id=None,
                                   **filters):
        """Return a QuerySet of watches having the given user or email, having
        (only) the given filters, and having the event_type and content_type
        attrs of the class.

        Matched Watches may be either confirmed and unconfirmed. They may
        include duplicates if the get-then-create race condition in notify()
        allowed them to be created.

        If you pass an email, it will be matched against only the email
        addresses of anonymous watches. At the moment, the only integration
        point planned between anonymous and registered watches is the claiming
        of anonymous watches of the same email address on user registration
        confirmation.

        If you pass the AnonymousUser, this will return an empty QuerySet.

        """
        # If we have trouble distinguishing subsets and such, we could store a
        # number_of_filters on the Watch.
        cls._validate_filters(filters)

        if isinstance(user_or_email, basestring):
            user_condition = Q(email=user_or_email)
        elif not user_or_email.is_anonymous():
            user_condition = Q(user=user_or_email)
        else:
            return Watch.objects.none()

        # Filter by stuff in the Watch row:
        watches = Watch.uncached.filter(
            user_condition,
            Q(content_type=ContentType.objects.get_for_model(cls.content_type))
                if cls.content_type
                else Q(),
            Q(object_id=object_id)
                if object_id
                else Q(),
            event_type=cls.event_type).extra(
                where=['(SELECT count(*) FROM notifications_watchfilter WHERE '
                       'notifications_watchfilter.watch_id='
                       'notifications_watch.id)=%s'],
                params=[len(filters)])
        # Optimization: If the subselect ends up being slow, store the number
        # of filters in each Watch row or try a GROUP BY.

        # Apply 1-to-many filters:
        for k, v in filters.iteritems():
            watches = watches.filter(filters__name=k,
                                     filters__value=hash_to_unsigned(v))

        return watches
Exemple #4
0
 def filter_conditions():
     """Return joins, WHERE conditions, and params to bind to them in
     order to check a notification against all the given filters."""
     # Not a one-liner. You're welcome. :-)
     self._validate_filters(filters)
     joins, wheres, join_params, where_params = [], [], [], []
     for n, (k, v) in enumerate(filters.iteritems()):
         joins.append('LEFT JOIN notifications_watchfilter f{n} '
                      'ON f{n}.watch_id=w.id '
                      'AND f{n}.name=%s'.format(n=n))
         join_params.append(k)
         wheres.append('(f{n}.value=%s '
                       'OR f{n}.value IS NULL)'.format(n=n))
         where_params.append(hash_to_unsigned(v))
     return joins, wheres, join_params + where_params
Exemple #5
0
 def filter_conditions():
     """Return joins, WHERE conditions, and params to bind to them in
     order to check a notification against all the given filters."""
     # Not a one-liner. You're welcome. :-)
     self._validate_filters(filters)
     joins, wheres, join_params, where_params = [], [], [], []
     for n, (k, v) in enumerate(filters.iteritems()):
         joins.append(
             'LEFT JOIN notifications_watchfilter f{n} '
             'ON f{n}.watch_id=w.id '
                 'AND f{n}.name=%s'.format(n=n))
         join_params.append(k)
         wheres.append('(f{n}.value=%s '
                       'OR f{n}.value IS NULL)'.format(n=n))
         where_params.append(hash_to_unsigned(v))
     return joins, wheres, join_params + where_params