annotate custom_search/receivers.py @ 989:2908859c2fe4

Smilies now use relative links. This is for upcoming switch to SSL. Currently we do not need absolute URLs for smilies. If this changes we can add it later.
author Brian Neal <bgneal@gmail.com>
date Thu, 29 Oct 2015 20:54:34 -0500
parents 78b459d4ab17
children
rev   line source
bgneal@753 1 """This module contains a custom Haystack signal processing class to update the
bgneal@753 2 search index in realtime. We update our search index by enqueuing edits and
bgneal@753 3 deletes into a queue for batch processing. Our class ensures we only enqueue
bgneal@753 4 content that should be in the search index.
bgneal@469 5
bgneal@469 6 """
bgneal@753 7 from django.db.models import signals
bgneal@753 8 import queued_search.signals
bgneal@924 9 import haystack
bgneal@469 10
bgneal@753 11 from bio.signals import profile_content_update
bgneal@753 12 from forums.signals import topic_content_update, post_content_update
bgneal@469 13
bgneal@753 14 import ygroup.models
bgneal@753 15 from weblinks.models import Link
bgneal@753 16 from podcast.models import Item
bgneal@753 17 from news.models import Story
bgneal@753 18 from downloads.models import Download
bgneal@753 19 from forums.models import Forum, Topic, Post
bgneal@753 20 from bio.models import UserProfile
bgneal@753 21
bgneal@753 22
bgneal@753 23 UID = 'custom_search.signals'
bgneal@753 24
bgneal@753 25
bgneal@753 26 class QueuedSignalProcessor(queued_search.signals.QueuedSignalProcessor):
bgneal@469 27 """
bgneal@753 28 This customized version of queued_search's QueuedSignalProcessor
bgneal@753 29 conditionally enqueues items to be indexed.
bgneal@469 30
bgneal@469 31 """
bgneal@753 32 def __init__(self, *args, **kwargs):
bgneal@753 33
bgneal@753 34 # We assume that it is okay to attempt to delete a model from the search
bgneal@753 35 # index even if the model object is not in the index. In other words,
bgneal@753 36 # attempting to delete an object from the index will not cause any
bgneal@753 37 # errors if it is not in the index. Thus if we see an object that has an
bgneal@753 38 # 'is_public' attribute, and it is false, we can safely enqueue a delete
bgneal@753 39 # in case the 'is_public' attribute just went from True to False. We
bgneal@753 40 # have no way of knowing that, it could have been False all along, but we
bgneal@753 41 # just try the delete in case to be safe.
bgneal@753 42
bgneal@753 43 # To make the code easier to read, use a table to drive our signal
bgneal@753 44 # connecting and disconnecting:
bgneal@753 45 self.signal_chain = [
bgneal@753 46 # Yahoo Group posts are always updated:
bgneal@753 47 (signals.post_save, ygroup.models.Post, self.enqueue_save),
bgneal@753 48 (signals.post_delete, ygroup.models.Post, self.enqueue_delete),
bgneal@753 49
bgneal@753 50 # Weblink Links are updated if they are public:
bgneal@753 51 (signals.post_save, Link, self.enqueue_public_save),
bgneal@753 52 (signals.post_delete, Link, self.enqueue_delete),
bgneal@753 53
bgneal@753 54 # Podcast Items are always updated:
bgneal@753 55 (signals.post_save, Item, self.enqueue_save),
bgneal@753 56 (signals.post_delete, Item, self.enqueue_delete),
bgneal@753 57
bgneal@753 58 # News Stories are always updated:
bgneal@753 59 (signals.post_save, Story, self.enqueue_save),
bgneal@753 60 (signals.post_delete, Story, self.enqueue_delete),
bgneal@753 61
bgneal@753 62 # Downloads are updated if they are public:
bgneal@753 63 (signals.post_save, Download, self.enqueue_public_save),
bgneal@753 64 (signals.post_delete, Download, self.enqueue_delete),
bgneal@753 65
bgneal@753 66 # Forum Topics are updated if they belong to a public forum:
bgneal@753 67 (topic_content_update, None, self.enqueue_topic_save),
bgneal@753 68 (signals.post_delete, Topic, self.enqueue_delete),
bgneal@753 69
bgneal@753 70 # Forum Posts are updated if they belong to a public forum:
bgneal@753 71 (post_content_update, None, self.enqueue_post_save),
bgneal@753 72 (signals.post_delete, Post, self.enqueue_delete),
bgneal@753 73
bgneal@753 74 # UserProfiles are updated when we receive a special signal:
bgneal@753 75 (profile_content_update, None, self.enqueue_profile),
bgneal@753 76 (signals.post_delete, UserProfile, self.enqueue_delete),
bgneal@753 77 ]
bgneal@753 78
bgneal@753 79 super(QueuedSignalProcessor, self).__init__(*args, **kwargs)
bgneal@753 80
bgneal@753 81 def setup(self):
bgneal@753 82 """We override setup() so we can attach signal handlers to only the
bgneal@753 83 models we search on. In some cases we have custom signals to tell us
bgneal@753 84 when to update the search index.
bgneal@469 85
bgneal@469 86 """
bgneal@753 87 for signal, sender, receiver in self.signal_chain:
bgneal@753 88 signal.connect(receiver, sender=sender, dispatch_uid=UID)
bgneal@469 89
bgneal@753 90 def teardown(self):
bgneal@753 91 """Disconnect all signals we previously connected."""
bgneal@753 92 for signal, sender, receiver in self.signal_chain:
bgneal@753 93 signal.disconnect(receiver, sender=sender, dispatch_uid=UID)
bgneal@753 94
bgneal@753 95 def enqueue_public_save(self, sender, instance, **kwargs):
bgneal@753 96 """Index only if the instance is_public.
bgneal@753 97
bgneal@753 98 If not, enqueue a delete just in case the is_public flag got flipped
bgneal@753 99 from True to False.
bgneal@469 100
bgneal@469 101 """
bgneal@753 102 if instance.is_public:
bgneal@753 103 self.enqueue_save(sender, instance, **kwargs)
bgneal@753 104 else:
bgneal@753 105 self.enqueue_delete(sender, instance, **kwargs)
bgneal@677 106
bgneal@753 107 def enqueue_topic_save(self, sender, **kwargs):
bgneal@753 108 """Enqueue only if the topic instance belongs to a public forum."""
bgneal@753 109 if sender.forum.id in Forum.objects.public_forum_ids():
bgneal@753 110 self.enqueue_save(Topic, sender, **kwargs)
bgneal@677 111
bgneal@753 112 def enqueue_post_save(self, sender, **kwargs):
bgneal@753 113 """Enqueue only if the post instance belongs to a public forum."""
bgneal@753 114 if sender.topic.forum.id in Forum.objects.public_forum_ids():
bgneal@753 115 self.enqueue_save(Post, sender, **kwargs)
bgneal@677 116
bgneal@753 117 def enqueue_profile(self, sender, **kwargs):
bgneal@753 118 """Forward the user profile instance on unconditionally."""
bgneal@753 119 self.enqueue_save(UserProfile, sender, **kwargs)
bgneal@924 120
bgneal@924 121
bgneal@924 122 # Starting with Django 1.7, we'd see Django generate warnings if we defined
bgneal@924 123 # a HAYSTACK_SIGNAL_PROCESSOR in our settings that referenced the class above.
bgneal@924 124 # This is because Haystack creates an instance of our signal processor class
bgneal@924 125 # (defined above) at import time, and thus imports this module very early in the
bgneal@924 126 # application startup sequence. Warnings are then generated when this module
bgneal@924 127 # imports our models, some of whose applications have not been imported yet.
bgneal@924 128 # This problem will presumably go away when Haystack can fully support Django
bgneal@924 129 # 1.7.x and implements an AppConfig with a ready() method. Until then, we don't
bgneal@924 130 # use Haystack's signal processor object; we'll just create one here. This
bgneal@924 131 # module will be imported when our custom_search app's ready() method runs.
bgneal@924 132
bgneal@924 133 signal_processor = QueuedSignalProcessor(haystack.connections,
bgneal@924 134 haystack.connection_router)