コード例 #1
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)
コード例 #2
0
ファイル: ConfigManager.py プロジェクト: beefone/pyload
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 (not user or(user and user.isAdmin())):
            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
            user = primary_uid(user)
            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()

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

        changed = False
        if section in self.parser and (not user or (user and user.isAdmin())):
            changed = self.parser.set(section, option, value, sync)
        else:
            # associated id
            user = primary_uid(user)
            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
                self.saveValues(user, section)

        if changed: self.core.evm.dispatchEvent("configChanged", 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=False):
        """ Deletes values saved in db and cached values for given user, NOT meta data
            Does not trigger an error when nothing was deleted. """
        user = user.primary if user else None
        if (user, section) in self.values:
            del self.values[user, section]

        self.db.deleteConfig(section, user)

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

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

        user = primary_uid(user)
        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 {}
コード例 #3
0
ファイル: ConfigParser.py プロジェクト: DasLampe/pyload
class ConfigParser:
    """
    Holds and manages the configuration + meta data for config read from file.
    """

    CONFIG = "pyload.conf"

    def __init__(self, config=None):

        if config: self.CONFIG = config

        # Meta data information
        self.config = OrderedDict()
        # The actual config values
        self.values = {}

        self.checkVersion()

        self.loadDefault()
        self.parseValues(self.CONFIG)

    def loadDefault(self):
        make_config(self)

    def checkVersion(self):
        """Determines if config needs to be deleted"""
        if exists(self.CONFIG):
            f = open(self.CONFIG, "rb")
            v = f.readline()
            f.close()
            v = v[v.find(":") + 1:].strip()

            if not v or int(v) < CONF_VERSION:
                f = open(self.CONFIG, "wb")
                f.write("version: " + str(CONF_VERSION))
                f.close()
                print "Old version of %s deleted" % self.CONFIG
        else:
            f = open(self.CONFIG, "wb")
            f.write("version:" + str(CONF_VERSION))
            f.close()

    def parseValues(self, filename):
        """read config values from file"""
        f = open(filename, "rb")
        config = f.readlines()[1:]

        # save the current section
        section = ""

        for line in config:
            line = line.strip()

            # comment line, different variants
            if not line or line.startswith("#") or line.startswith("//") or line.startswith(";"): continue

            if line.startswith("["):
                section = line.replace("[", "").replace("]", "")

                if section not in self.config:
                    print "Unrecognized section", section
                    section = ""

            else:
                name, non, value = line.rpartition("=")
                name = name.strip()
                value = value.strip()

                if not section:
                    print "Value without section", name
                    continue

                if name in self.config[section].config:
                    self.set(section, name, value, sync=False)
                else:
                    print "Unrecognized option", section, name


    def save(self):
        """saves config to filename"""

        configs = []
        f = open(self.CONFIG, "wb")
        configs.append(f)
        chmod(self.CONFIG, 0600)
        f.write("version: %i\n\n" % CONF_VERSION)

        for section, data in self.config.iteritems():
            f.write("[%s]\n" % section)

            for option, data in data.config.iteritems():
                value = self.get(section, option)
                if type(value) == unicode: value = value.encode("utf8")
                else: value = str(value)

                f.write('%s = %s\n' % (option, value))

            f.write("\n")

        f.close()

    def __getitem__(self, section):
        """provides dictionary like access: c['section']['option']"""
        return Section(self, section)

    def __contains__(self, section):
        """ checks if parser contains section """
        return section in self.config

    def get(self, section, option):
        """get value or default"""
        try:
            return self.values[section][option]
        except KeyError:
            return self.config[section].config[option].input.default_value

    def set(self, section, option, value, sync=True):
        """set value"""

        data = self.config[section].config[option]
        value = from_string(value, data.input.type)
        old_value = self.get(section, option)

        # only save when different values
        if value != old_value:
            if section not in self.values: self.values[section] = {}
            self.values[section][option] = value
            if sync:
                self.save()
            return True

        return False

    def getMetaData(self, section, option):
        """ get all config data for an option """
        return self.config[section].config[option]

    def iterSections(self):
        """ Yields section, config info, values, for all sections """

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

    def getSection(self, section):
        """ Retrieves single config as tuple (section, values) """
        return self.config[section], self.values[section] if section in self.values else {}

    def addConfigSection(self, section, label, desc, expl, config):
        """Adds a section to the config. `config` is a list of config tuples as used in plugin api defined as:
        The order of the config elements is preserved with OrderedDict
        """
        d = OrderedDict()

        for entry in config:
            if len(entry) != 4:
                raise ValueError("Config entry must be of length 4")

            # Values can have different roles depending on the two config formats
            conf_name, type_label, label_desc, default_input = entry

            # name, label, desc, input
            if isinstance(default_input, Input):
                input = default_input
                conf_label = type_label
                conf_desc = label_desc
            # name, type, label, default
            else:
                input = Input(to_input(type_label))
                input.default_value = from_string(default_input, input.type)
                conf_label = label_desc
                conf_desc = ""

            d[conf_name] = ConfigData(gettext(conf_label), gettext(conf_desc), input)

        data = SectionTuple(gettext(label), gettext(desc), gettext(expl), d)
        self.config[section] = data
コード例 #4
0
class ConfigParser:
    """
    Holds and manages the configuration + meta data for config read from file.
    """

    CONFIG = "pyload.conf"

    def __init__(self, config=None):

        if config: self.CONFIG = config

        # Meta data information
        self.config = OrderedDict()
        # The actual config values
        self.values = {}

        self.checkVersion()

        self.loadDefault()
        self.parseValues(self.CONFIG)

    def loadDefault(self):
        make_config(self)

    def checkVersion(self):
        """Determines if config needs to be deleted"""
        if exists(self.CONFIG):
            f = open(self.CONFIG, "rb")
            v = f.readline()
            f.close()
            v = v[v.find(":") + 1:].strip()

            if not v or int(v) < CONF_VERSION:
                f = open(self.CONFIG, "wb")
                f.write("version: " + str(CONF_VERSION))
                f.close()
                print "Old version of %s deleted" % self.CONFIG
        else:
            f = open(self.CONFIG, "wb")
            f.write("version:" + str(CONF_VERSION))
            f.close()

    def parseValues(self, filename):
        """read config values from file"""
        f = open(filename, "rb")
        config = f.readlines()[1:]

        # save the current section
        section = ""

        for line in config:
            line = line.strip()

            # comment line, different variants
            if not line or line.startswith("#") or line.startswith(
                    "//") or line.startswith(";"):
                continue

            if line.startswith("["):
                section = line.replace("[", "").replace("]", "")

                if section not in self.config:
                    print "Unrecognized section", section
                    section = ""

            else:
                name, non, value = line.rpartition("=")
                name = name.strip()
                value = value.strip()

                if not section:
                    print "Value without section", name
                    continue

                if name in self.config[section].config:
                    self.set(section, name, value, sync=False)
                else:
                    print "Unrecognized option", section, name

    def save(self):
        """saves config to filename"""

        configs = []
        f = open(self.CONFIG, "wb")
        configs.append(f)
        chmod(self.CONFIG, 0600)
        f.write("version: %i\n\n" % CONF_VERSION)

        for section, data in self.config.iteritems():
            f.write("[%s]\n" % section)

            for option, data in data.config.iteritems():
                value = self.get(section, option)
                if type(value) == unicode:
                    value = value.encode("utf8")
                else:
                    value = str(value)

                f.write('%s = %s\n' % (option, value))

            f.write("\n")

        f.close()

    def __getitem__(self, section):
        """provides dictionary like access: c['section']['option']"""
        return Section(self, section)

    def __contains__(self, section):
        """ checks if parser contains section """
        return section in self.config

    def get(self, section, option):
        """get value or default"""
        try:
            return self.values[section][option]
        except KeyError:
            return self.config[section].config[option].input.default_value

    def set(self, section, option, value, sync=True):
        """set value"""

        data = self.config[section].config[option]
        value = from_string(value, data.input.type)
        old_value = self.get(section, option)

        # only save when different values
        if value != old_value:
            if section not in self.values: self.values[section] = {}
            self.values[section][option] = value
            if sync:
                self.save()
            return True

        return False

    def getMetaData(self, section, option):
        """ get all config data for an option """
        return self.config[section].config[option]

    def iterSections(self):
        """ Yields section, config info, values, for all sections """

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

    def getSection(self, section):
        """ Retrieves single config as tuple (section, values) """
        return self.config[
            section], self.values[section] if section in self.values else {}

    def addConfigSection(self, section, label, desc, expl, config):
        """Adds a section to the config. `config` is a list of config tuple as used in plugin api defined as:
        The order of the config elements is preserved with OrderedDict
        """
        d = OrderedDict()

        for entry in config:
            name, data = to_configdata(entry)
            d[name] = data

        data = SectionTuple(gettext(label), gettext(desc), gettext(expl), d)
        self.config[section] = data
コード例 #5
0
ファイル: ConfigManager.py プロジェクト: sebmaynard/pyload
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
コード例 #6
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)