"""
The IModelEventWatcher defines handlers for CRUD operations on certain classes.
In the :py:meth:`assembl.models.generic.Content.send_to_changes`,
:py:meth:`assembl.models.auth.User.send_to_changes`,
:py:meth:`assembl.models.idea.Idea.send_to_changes` and
:py:meth:`assembl.models.idea_content_link.Extract.send_to_changes` methods,
we call the appropriate method on the current model watcher,
given by :py:func:`assembl.lib.sqla.get_model_watcher`, which is a
class implementing the :py:class:`IModelEventWatcher` protocol.
For now, the main usage is to ultimately call the
:py:class:`assembl.models.notification.ModelEventWatcherNotificationSubscriptionDispatcher`
upon :py:class:`Content` creation, either directly or indirectly.
Each process should call :py:func:`configure_model_watcher` with its process name,
so the proper model watcher is registered.
Different scenarios are defined in ``production.ini``:
1. Noop: just print
2. Direct: Invoke the :py:class:`assembl.models.notification.ModelEventWatcherNotificationSubscriptionDispatcher`
immediately in-thread. (May run into issues with closed transactions.)
3. Threaded: Send the CRUD event to another thread in the same process, using the :py:class:`assembl.tasks.threaded_model_watcher.ThreadedModelEventWatcher`
4. Broker (preferred): Send the event to the :py:class:`assembl.tasks.notification_dispatch.ModelEventWatcherCelerySender`, which will
send it through the celery machinery to the :py:class:`assembl.tasks.notification_dispatch.ModelEventWatcherCeleryReceiver`.
.. class:: IModelEventWatcher
An abstract interface for objects that receive CRUD events on some models
"""
from __future__ import print_function
from builtins import object
from zope import interface
from zope.component import getGlobalSiteManager
from pyramid.path import DottedNameResolver
from .logging import getLogger
log = getLogger()
resolver = DottedNameResolver(__package__)
[docs]class IModelEventWatcher(interface.Interface):
"""An abstract interface for objects that receive CRUD events on some models"""
def processPostCreated(self, id):
pass
def processIdeaCreated(self, id):
pass
def processIdeaModified(self, id, version):
pass
def processIdeaDeleted(self, id):
pass
def processExtractCreated(self, id):
pass
def processExtractModified(self, id, version):
pass
def processExtractDeleted(self, id):
pass
def processAccountCreated(self, id):
pass
def processAccountModified(self, id):
pass
[docs]@interface.implementer(IModelEventWatcher)
class BaseModelEventWatcher(object):
"""A dummy :py:class:`IModelEventWatcher` for testing purposes"""
def processPostCreated(self, id):
log.debug("processPostCreated: %d" % (id or 0))
def processIdeaCreated(self, id):
log.debug("processIdeaCreated: %d" % (id or 0))
def processIdeaModified(self, id, version):
log.debug("processIdeaModified: %d %d" % (id or 0, version or -1))
def processIdeaDeleted(self, id):
log.debug("processIdeaDeleted: %d" % (id or 0))
def processExtractCreated(self, id):
log.debug("processExtractCreated: %d" % (id or 0))
def processExtractModified(self, id, version):
log.debug("processExtractModified: %d %d" % (id or 0, version or -1))
def processExtractDeleted(self, id):
log.debug("processExtractDeleted: %d" % (id or 0))
def processAccountCreated(self, id):
log.debug("processAccountCreated: %d" % (id or 0))
def processAccountModified(self, id):
log.debug("processAccountModified: %d" % (id or 0))
[docs]@interface.implementer(IModelEventWatcher)
class CompositeModelEventWatcher(object):
"""A :py:class:`IModelEventWatcher` for that dispatches to others"""
def __init__(self, *watchers):
self.watchers = watchers
def processPostCreated(self, id):
for watcher in self.watchers:
watcher.processPostCreated(id)
def processIdeaCreated(self, id):
for watcher in self.watchers:
watcher.processIdeaCreated(id)
def processIdeaModified(self, id, version):
for watcher in self.watchers:
watcher.processIdeaModified(id, version)
def processIdeaDeleted(self, id):
for watcher in self.watchers:
watcher.processIdeaDeleted(id)
def processExtractCreated(self, id):
for watcher in self.watchers:
watcher.processExtractCreated(id)
def processExtractModified(self, id, version):
for watcher in self.watchers:
watcher.processExtractModified(id, version)
def processExtractDeleted(self, id):
for watcher in self.watchers:
watcher.processExtractDeleted(id)
def processAccountCreated(self, id):
for watcher in self.watchers:
watcher.processAccountCreated(id)
def processAccountModified(self, id):
for watcher in self.watchers:
watcher.processAccountModified(id)
_MODEL_WATCHER = None
[docs]def get_model_watcher():
"""Get the global implementation of py:class:`assembl.lib.model_watcherIModelEventWatcher`
for this process.
Often set in :py:func:`assembl.lib.model_watcher.configure_model_watcher`.
"""
global _MODEL_WATCHER
if _MODEL_WATCHER is None:
watchers = list(
getGlobalSiteManager().getAllUtilitiesRegisteredFor(IModelEventWatcher))
if not len(watchers):
watchers = (BaseModelEventWatcher(),)
if len(watchers) == 1:
_MODEL_WATCHER = watchers[0]
else:
_MODEL_WATCHER = CompositeModelEventWatcher(*watchers)
return _MODEL_WATCHER