Logo Search packages:      
Sourcecode: magicicada version File versions  Download package

syncdaemon.py

# syncdaemon.py
#
# Author: Facundo Batista <facundo@taniquetil.com.ar>
#
# Copyright 2010 Chicharreros
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 3, as published
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranties of
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
# PURPOSE.  See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program.  If not, see <http://www.gnu.org/licenses/>.

"""The backend that communicates Magicicada with the SyncDaemon."""

import logging

from twisted.internet import defer, reactor

from magicicada.dbusiface import DBusInterface
from magicicada.helpers import NO_OP

# log!
logger = logging.getLogger('magicicada.syncdaemon')


00032 class State(object):
    """Hold the state of SD."""
    _attrs =  ['name', 'description', 'is_error', 'is_connected',
               'is_online', 'queues', 'connection', 'is_started']

    def __init__(self):
        # starting defaults
        self.name = ''
        self.description = ''
        self.is_error = False
        self.is_connected = False
        self.is_online = False
        self.queues = ''
        self.connection = ''
        self.is_started = False

00048     def __getattribute__(self, name):
        """Return the value if there."""
        if name[0] == "_":
            return object.__getattribute__(self, name)
        else:
            return self.__dict__[name]

00055     def _set(self, **data):
        """Set the attributes from data, if allowed."""
        for name, value in data.iteritems():
            if name not in self._attrs:
                raise AttributeError("Name not in _attrs: %r" % name)
            self.__dict__[name] = value

00062     def __str__(self):
        """String representation."""
        result = []
        for attr in self._attrs:
            result.append("%s=%s" % (attr, getattr(self, attr)))
        return "<%s>" % ", ".join(result)


00070 class SyncDaemon(object):
    """Interface to Ubuntu One's SyncDaemon."""

    def __init__(self, dbus_class=DBusInterface):
        logger.info("SyncDaemon interface started!")

        # set up dbus and related stuff
        self.dbus = dbus_class(self)

        # attributes for GUI, definition and filling
        self.current_state = State()
        self.folders = None
        self.shares_to_me = None
        self.shares_to_others = None
        self.content_queue = None
        self.meta_queue = None

        # callbacks for GUI to hook in
        self.status_changed_callback = NO_OP
        self.content_queue_changed_callback = NO_OP
        self.meta_queue_changed_callback = NO_OP
        self.on_started_callback = NO_OP
        self.on_stopped_callback = NO_OP
        self.on_connected_callback = NO_OP
        self.on_disconnected_callback = NO_OP
        self.on_online_callback = NO_OP
        self.on_offline_callback = NO_OP
        self.on_folders_changed_callback = NO_OP
        self.on_shares_to_me_changed_callback = NO_OP
        self.on_shares_to_others_changed_callback = NO_OP

        # mq needs to be polled to know progress
        self._mqcaller = None
        self._mq_poll_time = 5   # seconds

        # load initial data if ubuntuone-client already started
        if self.dbus.is_sd_started():
            self.current_state._set(is_started=True)
            self._get_initial_data()
        else:
            self.current_state._set(is_started=False)


00113     def shutdown(self):
        """Shut down the SyncDaemon."""
        logger.info("SyncDaemon interface going down")
        self.dbus.shutdown()

        # cancel the mq polling caller, if any
        if self._mqcaller is not None and self._mqcaller.active():
            self._mqcaller.cancel()

    @defer.inlineCallbacks
00123     def _get_initial_data(self):
        """Get the initial SD data."""
        logger.info("Getting initial data")

        status_data = yield self.dbus.get_status()
        self._send_status_changed(*status_data)

        self.content_queue = yield self.dbus.get_content_queue()
        self.content_queue_changed_callback(self.content_queue)

        self.meta_queue = yield self.dbus.get_meta_queue()
        self.meta_queue_changed_callback(self.meta_queue)

        self.folders = yield self.dbus.get_folders()

        self.shares_to_me = yield self.dbus.get_shares_to_me()
        self.shares_to_others = yield self.dbus.get_shares_to_others()

    @defer.inlineCallbacks
00142     def on_sd_shares_changed(self):
        """Shares changed, ask for new information."""
        logger.info("SD Shares changed")

        # to me
        new_to_me = yield self.dbus.get_shares_to_me()
        if new_to_me != self.shares_to_me:
            self.shares_to_me = new_to_me
            self.on_shares_to_me_changed_callback(new_to_me)

        # to others
        new_to_others = yield self.dbus.get_shares_to_others()
        if new_to_others != self.shares_to_others:
            self.shares_to_others = new_to_others
            self.on_shares_to_others_changed_callback(new_to_others)

    @defer.inlineCallbacks
00159     def on_sd_folders_changed(self):
        """Folders changed, ask for new information."""
        logger.info("SD Folders changed")
        self.folders = yield self.dbus.get_folders()
        self.on_folders_changed_callback(self.folders)

00165     def on_sd_name_owner_changed(self, now_active):
        """SyncDaemon name owner changed."""
        logger.info("SD Name Owner changed: %s", now_active)
        self.current_state._set(is_started=now_active)

        def set_status(name, description):
            """Set status after the name owner change."""
            d = dict(name=name, description=description, is_error=False,
                     is_connected=False, is_online=False, queues='',
                     connection='')
            self.current_state._set(**d)

        if now_active:
            set_status('STARTED', 'ubuntuone-client just started')
            self.on_started_callback()
            self._get_initial_data()
        else:
            set_status('STOPPED', 'ubuntuone-client is stopped')
            self.on_stopped_callback()

00185     def on_sd_status_changed(self, *status_data):
        """The Status of SD changed.."""
        logger.info("SD Status changed")
        self._send_status_changed(*status_data)

    def _send_status_changed(self, name, description, is_error, is_connected,
                             is_online, queues, connection):
        logger.debug("    new status: name=%r, description=%r, is_error=%s, "
                     "is_connected=%s, is_online=%s, queues=%r, connection=%r",
                     name, description, is_error, is_connected, is_online,
                     queues, connection)

        # check status changes to call other callbacks
        if is_connected and not self.current_state.is_connected:
            self.on_connected_callback()
        if not is_connected and self.current_state.is_connected:
            self.on_disconnected_callback()
        if is_online and not self.current_state.is_online:
            self.on_online_callback()
        if not is_online and self.current_state.is_online:
            self.on_offline_callback()

        # set current state to new values and call status changed cb
        self.current_state._set(name=name, description=description,
                                is_error=is_error, is_connected=is_connected,
                                is_online=is_online, queues=queues,
                                connection=connection)
        self.status_changed_callback(name, description, is_error, is_connected,
                                     is_online, queues, connection)

        # if corresponds, supervise MQ
        self._check_mq()

    @defer.inlineCallbacks
00219     def on_sd_content_queue_changed(self):
        """Content Queue changed, ask for new information."""
        logger.info("SD Content Queue changed")
        new_cq = yield self.dbus.get_content_queue()
        if new_cq != self.content_queue:
            logger.info("Content Queue info is new! %d items", len(new_cq))
            self.content_queue = new_cq
            self.content_queue_changed_callback(new_cq)

    @defer.inlineCallbacks
00229     def _check_mq(self):
        """Check MQ if we should."""
        state = self.current_state
        if state.queues not in ('WORKING_ON_METADATA', 'WORKING_ON_BOTH'):
            logger.info("Check MQ called but States not in MQ")
        else:
            logger.info("Asking for MQ information")

            # have we a previous call later still running?
            if self._mqcaller is not None and self._mqcaller.active():
                self._mqcaller.cancel()

            # get the info
            new_mq = yield self.dbus.get_meta_queue()

            if new_mq != self.meta_queue:
                logger.info("SD Meta Queue changed: %d items", len(new_mq))
                self.meta_queue = new_mq
                self.meta_queue_changed_callback(new_mq)

            # check again later
            self._mqcaller = reactor.callLater(self._mq_poll_time,
                                               self._check_mq)

00253     def start(self):
        """Start the SyncDaemon."""
        logger.info("Starting u1.SD")
        self.dbus.start()
        self._get_initial_data()

00259     def quit(self):
        """Stop the SyncDaemon and makes it quit."""
        logger.info("Stopping u1.SD")
        self.dbus.quit()

00264     def connect(self):
        """Tell the SyncDaemon that the user wants it to connect."""
        logger.info("Telling u1.SD to connect")
        self.dbus.connect()

00269     def disconnect(self):
        """Tell the SyncDaemon that the user wants it to disconnect."""
        logger.info("Telling u1.SD to disconnect")
        self.dbus.disconnect()

Generated by  Doxygen 1.6.0   Back to index