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
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)