Exemple #1
0
class ConfigManager(ConfigParser):
    """ Manages the core config and configs for addons and single user.
        Has similar interface to ConfigParser. """

    def __init__(self, core, parser):
        # No __init__ call to super class is needed!

        self.core = core
        self.db = core.db
        # The config parser, holding the core config
        self.parser = parser

        # similar to parser, separated meta data and values
        self.config = OrderedDict()

        # Value cache for multiple user configs
        # Values are populated from db on first access
        # Entries are saved as (user, section) keys
        self.values = {}
        # TODO: similar to a cache, could be deleted periodically

    def save(self):
        self.parser.save()

    @convertKeyError
    def get(self, section, option, user=None):
        """get config value, core config only available for admins.
        if user is not valid default value will be returned"""

        # Core config loaded from parser, when no user is given or he is admin
        if section in self.parser and user is None:
            return self.parser.get(section, option)
        else:
            # We need the id and not the instance
            # Will be None for admin user and so the same as internal access
            try:
                # Check if this config exists
                # Configs without meta data can not be loaded!
                data = self.config[section].config[option]
                self.loadValues(user, section)
                return self.values[user, section][option]
            except KeyError:
                pass # Returns default value later

        return self.config[section].config[option].default

    def loadValues(self, user, section):
        if (user, section) not in self.values:
            conf = self.db.loadConfig(section, user)
            try:
                self.values[user, section] = json.loads(conf) if conf else {}
            except ValueError: # Something did go wrong when parsing
                self.values[user, section] = {}
                self.core.print_exc()

        return self.values[user, section]

    @convertKeyError
    def set(self, section, option, value, sync=True, user=None):
        """ set config value  """

        changed = False
        if section in self.parser and user is None:
            changed = self.parser.set(section, option, value, sync)
        else:
            data = self.config[section].config[option]
            value = from_string(value, data.type)
            old_value = self.get(section, option)

            # Values will always be saved to db, sync is ignored
            if value != old_value:
                changed = True
                self.values[user, section][option] = value
                if sync: self.saveValues(user, section)

        if changed: self.core.evm.dispatchEvent("config:changed", section, option, value)
        return changed

    def saveValues(self, user, section):
        self.db.saveConfig(section, json.dumps(self.values[user, section]), user)

    def delete(self, section, user=None):
        """ Deletes values saved in db and cached values for given user, NOT meta data
            Does not trigger an error when nothing was deleted. """
        if (user, section) in self.values:
            del self.values[user, section]

        self.db.deleteConfig(section, user)
        self.core.evm.dispatchEvent("config:deleted", section, user)

    def iterCoreSections(self):
        return self.parser.iterSections()

    def iterSections(self, user=None):
        """ Yields: section, metadata, values """
        values = self.db.loadConfigsForUser(user)

        # Every section needs to be json decoded
        for section, data in values.items():
            try:
                values[section] = json.loads(data) if data else {}
            except ValueError:
                values[section] = {}
                self.core.print_exc()

        for name, config in self.config.iteritems():
            yield name, config, values[name] if name in values else {}

    def getSection(self, section, user=None):
        if section in self.parser and user is None:
            return self.parser.getSection(section)

        values = self.loadValues(user, section)
        return self.config.get(section), values
Exemple #2
0
class InteractionManager:
    """
    Class that gives ability to interact with the user.
    Arbitrary tasks with predefined output and input types can be set off.
    """

    # number of seconds a client is classified as active
    CLIENT_THRESHOLD = 60
    NOTIFICATION_TIMEOUT = 60 * 60 * 30
    MAX_NOTIFICATIONS = 50

    def __init__(self, core):
        self.lock = Lock()
        self.core = core
        self.tasks = OrderedDict()  #task store, for all outgoing tasks

        self.last_clients = {}
        self.ids = 0  #uniue interaction ids

    def isClientConnected(self, user):
        return self.last_clients.get(user, 0) + self.CLIENT_THRESHOLD > time()

    @lock
    def work(self):
        # old notifications will be removed
        for n in [k for k, v in self.tasks.iteritems() if v.timedOut()]:
            del self.tasks[n]

        # keep notifications count limited
        n = [k for k, v in self.tasks.iteritems() if v.type == IA.Notification]
        n.reverse()
        for v in n[:self.MAX_NOTIFICATIONS]:
            del self.tasks[v]

    @lock
    def createNotification(self,
                           title,
                           content,
                           desc="",
                           plugin="",
                           owner=None):
        """ Creates and queues a new Notification

        :param title: short title
        :param content: text content
        :param desc: short form of the notification
        :param plugin: plugin name
        :return: :class:`InteractionTask`
        """
        task = InteractionTask(self.ids,
                               IA.Notification,
                               Input(InputType.Text, None, content),
                               title,
                               desc,
                               plugin,
                               owner=owner)
        self.ids += 1
        self.queueTask(task)
        return task

    @lock
    def createQueryTask(self, input, desc, plugin="", owner=None):
        # input type was given, create a input widget
        if type(input) == int:
            input = Input(input)
        if not isinstance(input, Input):
            raise TypeError("'Input' class expected not '%s'" % type(input))

        task = InteractionTask(self.ids,
                               IA.Query,
                               input,
                               _("Query"),
                               desc,
                               plugin,
                               owner=owner)
        self.ids += 1
        self.queueTask(task)
        return task

    @lock
    def createCaptchaTask(self,
                          img,
                          format,
                          filename,
                          plugin="",
                          type=InputType.Text,
                          owner=None):
        """ Createss a new captcha task.

        :param img: image content (not base encoded)
        :param format: img format
        :param type: :class:`InputType`
        :return:
        """
        if type == 'textual':
            type = InputType.Text
        elif type == 'positional':
            type = InputType.Click

        input = Input(type, data=[standard_b64encode(img), format, filename])

        #todo: title desc plugin
        task = InteractionTask(self.ids,
                               IA.Captcha,
                               input,
                               _("Captcha request"),
                               _("Please solve the captcha."),
                               plugin,
                               owner=owner)

        self.ids += 1
        self.queueTask(task)
        return task

    @lock
    def removeTask(self, task):
        if task.iid in self.tasks:
            del self.tasks[task.iid]
            self.core.evm.dispatchEvent("interaction:deleted", task.iid)

    @lock
    def getTaskByID(self, iid):
        return self.tasks.get(iid, None)

    @lock
    def getTasks(self, user, mode=IA.All):
        # update last active clients
        self.last_clients[user] = time()

        # filter current mode
        tasks = [
            t for t in self.tasks.itervalues()
            if mode == IA.All or bits_set(t.type, mode)
        ]
        # filter correct user / or shared
        tasks = [
            t for t in tasks if user is None or user == t.owner or t.shared
        ]

        return tasks

    def isTaskWaiting(self, user, mode=IA.All):
        tasks = [
            t for t in self.getTasks(user, mode)
            if not t.type == IA.Notification or not t.seen
        ]
        return len(tasks) > 0

    def queueTask(self, task):
        cli = self.isClientConnected(task.owner)

        # set waiting times based on threshold
        if cli:
            task.setWaiting(self.CLIENT_THRESHOLD)
        else:  # TODO: higher threshold after client connects?
            task.setWaiting(self.CLIENT_THRESHOLD / 3)

        if task.type == IA.Notification:
            task.setWaiting(
                self.NOTIFICATION_TIMEOUT)  # notifications are valid for 30h

        for plugin in self.core.addonManager.activePlugins():
            try:
                plugin.newInteractionTask(task)
            except:
                self.core.print_exc()

        self.tasks[task.iid] = task
        self.core.evm.dispatchEvent("interaction:added", task)
class InteractionManager:
    """
    Class that gives ability to interact with the user.
    Arbitrary tasks with predefined output and input types can be set off.
    """

    # number of seconds a client is classified as active
    CLIENT_THRESHOLD = 60
    NOTIFICATION_TIMEOUT = 60 * 60 * 30
    MAX_NOTIFICATIONS = 50

    def __init__(self, core):
        self.lock = Lock()
        self.core = core
        self.tasks = OrderedDict() #task store, for all outgoing tasks

        self.last_clients = {}
        self.ids = 0 #uniue interaction ids

    def isClientConnected(self, user):
        return self.last_clients.get(user, 0) + self.CLIENT_THRESHOLD > time()

    @lock
    def work(self):
        # old notifications will be removed
        for n in [k for k, v in self.tasks.iteritems() if v.timedOut()]:
            del self.tasks[n]

        # keep notifications count limited
        n = [k for k,v in self.tasks.iteritems() if v.type == IA.Notification]
        n.reverse()
        for v in n[:self.MAX_NOTIFICATIONS]:
            del self.tasks[v]

    @lock
    def createNotification(self, title, content, desc="", plugin="", owner=None):
        """ Creates and queues a new Notification

        :param title: short title
        :param content: text content
        :param desc: short form of the notification
        :param plugin: plugin name
        :return: :class:`InteractionTask`
        """
        task = InteractionTask(self.ids, IA.Notification, Input(InputType.Text, None, content), title, desc, plugin,
                               owner=owner)
        self.ids += 1
        self.queueTask(task)
        return task

    @lock
    def createQueryTask(self, input, desc, plugin="", owner=None):
        # input type was given, create a input widget
        if type(input) == int:
            input = Input(input)
        if not isinstance(input, Input):
            raise TypeError("'Input' class expected not '%s'" % type(input))

        task = InteractionTask(self.ids, IA.Query, input, _("Query"), desc, plugin, owner=owner)
        self.ids += 1
        self.queueTask(task)
        return task

    @lock
    def createCaptchaTask(self, img, format, filename, plugin="", type=InputType.Text, owner=None):
        """ Createss a new captcha task.

        :param img: image content (not base encoded)
        :param format: img format
        :param type: :class:`InputType`
        :return:
        """
        if type == 'textual':
            type = InputType.Text
        elif type == 'positional':
            type = InputType.Click

        input = Input(type, data=[standard_b64encode(img), format, filename])

        #todo: title desc plugin
        task = InteractionTask(self.ids, IA.Captcha, input,
                            _("Captcha request"), _("Please solve the captcha."), plugin, owner=owner)

        self.ids += 1
        self.queueTask(task)
        return task

    @lock
    def removeTask(self, task):
        if task.iid in self.tasks:
            del self.tasks[task.iid]
            self.core.evm.dispatchEvent("interaction:deleted", task.iid)

    @lock
    def getTaskByID(self, iid):
        return self.tasks.get(iid, None)

    @lock
    def getTasks(self, user, mode=IA.All):
        # update last active clients
        self.last_clients[user] = time()

        # filter current mode
        tasks = [t for t in self.tasks.itervalues() if mode == IA.All or bits_set(t.type, mode)]
        # filter correct user / or shared
        tasks = [t for t in tasks if user is None or user == t.owner or t.shared]

        return tasks

    def isTaskWaiting(self, user, mode=IA.All):
        tasks = [t for t in self.getTasks(user, mode) if not t.type == IA.Notification or not t.seen]
        return len(tasks) > 0

    def queueTask(self, task):
        cli = self.isClientConnected(task.owner)

        # set waiting times based on threshold
        if cli:
            task.setWaiting(self.CLIENT_THRESHOLD)
        else: # TODO: higher threshold after client connects?
            task.setWaiting(self.CLIENT_THRESHOLD / 3)

        if task.type == IA.Notification:
            task.setWaiting(self.NOTIFICATION_TIMEOUT) # notifications are valid for 30h

        for plugin in self.core.addonManager.activePlugins():
            try:
                plugin.newInteractionTask(task)
            except:
                self.core.print_exc()

        self.tasks[task.iid] = task
        self.core.evm.dispatchEvent("interaction:added", task)