Exemple #1
0
# coding=utf-8
""""""
from blinker.base import Namespace

# pylint: disable=C0103
#         invalid constant name

ns = Namespace()

#: sent when membership is set. Sender is community, arguments are:
#: :class:`.models.Membership` instance, :bool:`is_new`
membership_set = ns.signal("membership_set")

#: sent just before membership is removed. Sender is community, arguments:
# :class:`.models.Membership` instance
membership_removed = ns.signal("membership_removed")
class LiveData(object):
    def __init__(self, pyrebase_app, root_path, ttl=None, retry_interval=None):
        self._app = pyrebase_app
        self._root_path = root_path
        self._ttl = ttl
        self._retry_interval = (RETRY_INTERVAL
                                if retry_interval is None else retry_interval)
        self._db = self._app.database()
        self._streams = {}
        self._gc_streams = queue.Queue()
        self._gc_thread = None
        self._cache = None
        self.events = Namespace()

        self._handlers = {
            'put': self._put_handler,
            'patch': self._patch_handler,
        }

    def get_data(self):
        if self._cache is None:
            # Fetch data now
            value = self._db.child(self._root_path).get().val()
            self._cache = data.FirebaseData(value)
            # Listen for updates
            self.listen()

        return self._cache

    def get_data_silent(self):
        try:
            return self.get_data()
        except Exception:
            logger.exception('Error getting data')

    def set_data(self, path, value):
        path_list = data.get_path_list(path)
        child = self._db.child(self._root_path)

        for path_part in path_list:
            child = child.child(path_part)
        child.set(value)

    def is_stale(self):
        if self._ttl is None:
            return False

        data = self.get_data()
        if data is None or data.last_updated_at is None:
            logger.debug('Data is invalid: %s', data)
            return True

        stale = datetime.datetime.utcnow() - data.last_updated_at > self._ttl
        if stale:
            logger.debug('Data is stale: %s', data)
        else:
            logger.debug('Data is fresh: %s', data)
        return stale

    def signal(self, path, doc=None):
        norm_path = data.normalize_path(path)
        return self.events.signal(norm_path, doc=doc)

    def listen(self):
        stream = self._db.child(self._root_path).stream(self._stream_handler)
        self._streams[id(stream)] = stream
        self._start_stream_gc()
        watcher.watch(id(self),
                      self.is_stale,
                      self.restart,
                      interval=self._ttl)
        # If the stream and stale watcher are established,
        # the metawatcher is no longer needed.
        self.cancel_metawatcher()

    def get_metawatcher_name(self):
        return 'meta_{}'.format(id(self))

    def start_metawatcher(self):
        watcher.watch(self.get_metawatcher_name(),
                      lambda: self._cache is None,
                      self.get_data_silent,
                      interval=self._retry_interval)

    def cancel_metawatcher(self):
        watcher.cancel(self.get_metawatcher_name())

    def restart(self):
        self.reset()
        self.start_metawatcher()
        self.get_data_silent()

    def reset(self):
        logger.debug('Resetting all data')
        self.hangup(block=False)
        self._cache = None

    def hangup(self, block=True):
        logger.debug('Marking all streams for shut down')

        watcher.cancel(id(self))

        for stream in self._streams.values():
            self._gc_streams.put(stream)

        if block:
            self._gc_streams.join()

    def _set_path_value(self, path, value):
        data = self.get_data()
        data.set(path, value)
        self._recurse_signal(path)

    def _recurse_signal(self, path):
        path_list = data.get_path_list(path)
        partial_path = ''
        value = self.get_data()

        self.signal('/').send(value, value=value.get(), path=path)
        for part in path_list:
            partial_path = '/'.join((partial_path, part))
            self.signal(partial_path).send(value,
                                           value=value.get(partial_path),
                                           path=path)

    def _put_handler(self, path, value):
        logger.debug('PUT: path=%s data=%s', path, value)
        self._set_path_value(path, value)

    def _patch_handler(self, path, all_values):
        logger.debug('PATCH: path=%s data=%s', path, all_values)

        for rel_path, value in all_values.items():
            full_path = data.normalize_path('{}/{}'.format(path, rel_path))
            self._set_path_value(full_path, value)

    def _valid_message(self, message):
        required_keys = [
            'event',
            'path',
            'data',
        ]

        valid_keys = all(k in message for k in required_keys)
        if not valid_keys:
            return False

        if message['event'] not in self._handlers:
            return False

        return True

    def _stream_handler(self, message):
        logger.debug('STREAM received: %s', message)
        if not self._valid_message(message):
            logger.warn('Invalid message: %s', message)
            return

        handler = self._handlers[message['event']]
        handler(message['path'], message['data'])

    def _gc_stream_worker(self):
        while True:
            stream = self._gc_streams.get()
            logger.debug('Closing stream: %s', stream)

            try:
                stream.close()
                del self._streams[id(stream)]
            except Exception as e:
                logger.warning('Error closing stream %s: %s', stream, e)
            else:
                logger.debug('Stream closed: %s', stream)

            self._gc_streams.task_done()

    def _start_stream_gc(self):
        if self._gc_thread is None:
            self._gc_thread = threading.Thread(target=self._gc_stream_worker,
                                               daemon=True)
            self._gc_thread.start()
Exemple #3
0
Signals are the main tools used for decoupling applications components by
sending notifications. In short, signals allow certain senders to notify
subscribers that something happened.

Cf. http://flask.pocoo.org/docs/signals/ for detailed documentation.

The main signal is currently :obj:`activity`.
"""

from blinker.base import Namespace

signals = Namespace()

#: Triggered at application initialization when all extensions and plugins have
#: been loaded
components_registered = signals.signal("app:components:registered")

#: Trigger when JS api must be registered. At this time :func:`flask.url_for` is
#: usable
register_js_api = signals.signal("app:register-js-api")

#: This signal is used by the activity streams service and its clients.
activity = signals.signal("activity")

#: This signal is sent when user object has been loaded. g.user and current_user
#: are available.
user_loaded = signals.signal("user_loaded")

auth_failed = signals.signal("auth_failed")
Exemple #4
0
from blinker.base import Namespace
SIG_ROLES_EDITED = 'roles-edited'
SIG_ROLES_ADDED = 'roles-added'
SIG_ACC_CREATED = 'acc-created'
SIG_ACC_STATUS_CHANGE = 'acc-status-change'

waitlist_bps = Namespace()

roles_changed_sig = waitlist_bps.signal(
    SIG_ROLES_EDITED, "Called when roles are changed on an account")
account_created_sig = waitlist_bps.signal(
    SIG_ACC_CREATED, 'Called when a new Waitlist Account is created')
account_status_change_sig = waitlist_bps.signal(
    SIG_ACC_STATUS_CHANGE, 'Called when an account is enabled or disabled')
roles_added_sig = waitlist_bps.signal(SIG_ROLES_ADDED,
                                      'Called when a new role is added')


def send_roles_changed(sender, to_id, by_id, added_roles, removed_roles, note):
    roles_changed_sig.send(sender,
                           to_id=to_id,
                           by_id=by_id,
                           added_roles=added_roles,
                           removed_roles=removed_roles,
                           note=note)


def send_roles_added(sender, by_id, role_name, role_display_name):
    roles_added_sig.send(sender,
                         by_id=by_id,
                         role_name=role_name,
Exemple #5
0
# coding=utf-8
""""""
from __future__ import absolute_import, print_function, unicode_literals

from blinker.base import Namespace

# pylint: disable=C0103
#         invalid constant name

ns = Namespace()

#: sent when membership is set. Sender is community, arguments are:
#: :class:`.models.Membership` instance, :bool:`is_new`
membership_set = ns.signal("membership_set")

#: sent just before membership is removed. Sender is community, arguments:
# :class:`.models.Membership` instance
membership_removed = ns.signal("membership_removed")
Exemple #6
0
           'Called when an account is enabled or disabled')
role_created_sig = waitlist_bps.\
    signal(SIG_ROLES_ADDED,
           'Called when a new role is created')
role_removed_sig = waitlist_bps.\
    signal(SIG_ROLES_REMOVED,
           'Called when a role is removed')

alt_link_removed_sig = waitlist_bps.\
    signal(SIG_ALT_LINK_REMOVED,
           'Called when a link from an account to a character was removed')
alt_link_added_sig = waitlist_bps.\
    signal(SIG_ALT_LINK_ADDED,
           'Called when a link from an account to a character was added')

account_name_change_sig = waitlist_bps.signal(
    'Called when the name of an account gets changed')


def send_roles_changed(sender, to_id, by_id, added_roles, removed_roles, note):
    roles_changed_sig.send(sender,
                           to_id=to_id,
                           by_id=by_id,
                           added_roles=added_roles,
                           removed_roles=removed_roles,
                           note=note)


def send_role_created(sender, by_id, role_name, role_display_name):
    role_created_sig.send(sender,
                          by_id=by_id,
                          role_name=role_name,
Exemple #7
0
subscribers that something happened.

Cf. http://flask.pocoo.org/docs/signals/ for detailed documentation.

The main signal is currently :obj:`activity`.
"""

from __future__ import absolute_import

from blinker.base import Namespace

signals = Namespace()

#: Triggered at application initialization when all extensions and plugins have
#: been loaded
components_registered = signals.signal("app:components:registered")

#: This signal is used by the activity streams service and its clients.
activity = signals.signal("activity")


#: Currently not used and subject to change.
entity_created = signals.signal("entity:created")

#: Currently not used and subject to change.
entity_updated = signals.signal("entity:updated")

#: Currently not used and subject to change.
entity_deleted = signals.signal("entity:deleted")

#user_created = signals.signal("user:created")
Exemple #8
0
#!/usr/bin/env python
# coding=utf8

__version__ = '0.5.dev1'

from blinker.base import Namespace, Signal

namespace = Namespace()

user_id_changed = namespace.signal('user_id_changed')
user_id_reset = namespace.signal('user_id_reset')
Exemple #9
0
"""
All signals used by Abilian Core.

Cf. http://flask.pocoo.org/docs/signals/ for detailed documentation.

These signals are currently not used, and for this reason subject to change.
"""

from blinker.base import Namespace

signals = Namespace()

entity_created = signals.signal("entity:created")
entity_updated = signals.signal("entity:updated")
entity_deleted = signals.signal("entity:deleted")

#user_created = signals.signal("user:created")
#user_deleted = signals.signal("user:deleted")

activity = signals.signal("activity")
Exemple #10
0
Signals are the main tools used for decoupling applications components by
sending notifications. In short, signals allow certain senders to notify
subscribers that something happened.

Cf. http://flask.pocoo.org/docs/signals/ for detailed documentation.

The main signal is currently :obj:`activity`.
"""

from blinker.base import Namespace

signals = Namespace()

#: Triggered at application initialization when all extensions and plugins have
#: been loaded
components_registered = signals.signal("app:components:registered")

#: Trigger when JS api must be registered. At this time :func:`flask.url_for` is
#: usable
register_js_api = signals.signal("app:register-js-api")

#: This signal is used by the activity streams service and its clients.
activity = signals.signal("activity")

#: This signal is sent when user object has been loaded. g.user and current_user
#: are available.
user_loaded = signals.signal("user_loaded")

auth_failed = signals.signal("auth_failed")
Exemple #11
0
Signals are the main tools used for decoupling applications components by
sending notifications. In short, signals allow certain senders to notify
subscribers that something happened.

Cf. http://flask.pocoo.org/docs/signals/ for detailed documentation.

The main signal is currently :obj:`activity`.
"""

from __future__ import absolute_import, print_function, division

from blinker.base import Namespace

signals = Namespace()

#: Triggered at application initialization when all extensions and plugins have
#: been loaded
components_registered = signals.signal("app:components:registered")

#: Trigger when JS api must be registered. At this time :func:`flask.url_for` is
#: usable
register_js_api = signals.signal('app:register-js-api')

#: This signal is used by the activity streams service and its clients.
activity = signals.signal("activity")

#: This signal is sent when user object has been loaded. g.user and current_user
#: are available.
user_loaded = signals.signal('user_loaded')
Exemple #12
0
# coding=utf-8
"""
"""
from __future__ import absolute_import

from blinker.base import Namespace

# pylint: disable=C0103
#         invalid constant name

ns = Namespace()

#: sent when membership is set. Sender is community, arguments are:
#: :class:`.models.Membership` instance, :bool:`is_new`
membership_set = ns.signal('membership_set')

#: sent just before membership is removed. Sender is community, arguments:
# :class:`.models.Membership` instance
membership_removed = ns.signal('membership_removed')
Exemple #13
0
Signals are the main tools used for decoupling applications components by
sending notifications. In short, signals allow certain senders to notify
subscribers that something happened.

Cf. http://flask.pocoo.org/docs/signals/ for detailed documentation.

The main signal is currently :obj:`activity`.
"""

from __future__ import absolute_import, division, print_function, \
    unicode_literals

from blinker.base import Namespace

signals = Namespace()

#: Triggered at application initialization when all extensions and plugins have
#: been loaded
components_registered = signals.signal("app:components:registered")

#: Trigger when JS api must be registered. At this time :func:`flask.url_for` is
#: usable
register_js_api = signals.signal('app:register-js-api')

#: This signal is used by the activity streams service and its clients.
activity = signals.signal("activity")

#: This signal is sent when user object has been loaded. g.user and current_user
#: are available.
user_loaded = signals.signal('user_loaded')