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 __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 __init__(self, core): self.lock = Lock() self.core = core self.tasks = OrderedDict() #task store, for outgoing tasks only self.notifications = [] #list of notifications self.last_clients = { Output.Notification: 0, Output.Captcha: 0, Output.Query: 0, } self.ids = 0 #only for internal purpose
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 = {}
def addConfigSection(self, section, name, desc, long_desc, config, base=False): """Adds a section to the config. `config` is a list of config tuples as used in plugin api defined as: Either (name, type, verbose_name, default_value) or (name, type, verbose_name, short_description, default_value) The order of the config elements is preserved with OrderedDict """ d = OrderedDict() for entry in config: if len(entry) == 5: conf_name, type, conf_desc, conf_verbose, default = entry else: # config options without tooltip / description conf_name, type, conf_desc, default = entry conf_verbose = "" d[conf_name] = ConfigData(gettext(conf_desc), type, gettext(conf_verbose), from_string(default, type)) if base: if section not in self.baseSections: self.baseSections.append(section) elif section in self.baseSections: return # would overwrite base section data = SectionTuple(gettext(name), gettext(desc), gettext(long_desc), d) self.config[section] = data if section not in self.values: self.values[section] = {}
def getAllPackages(self, root=None, owner=None, tags=None): """ Return dict with package information :param root: optional root to filter :param owner: optional user id :param tags: optional tag list """ qry = ('SELECT pid, name, folder, root, owner, site, comment, password, added, tags, status, packageorder ' 'FROM packages%s ORDER BY root, packageorder') if root is None: stats = self.getPackageStats(owner=owner) if owner is None: self.c.execute(qry % "") else: self.c.execute(qry % " WHERE owner=?", (owner,)) else: stats = self.getPackageStats(root=root, owner=owner) if owner is None: self.c.execute(qry % ' WHERE root=? OR pid=?', (root, root)) else: self.c.execute(qry % ' WHERE (root=? OR pid=?) AND owner=?', (root, root, owner)) data = OrderedDict() for r in self.c: data[r[0]] = PackageInfo( r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7], r[8], r[9].split(","), r[10], r[11], stats.get(r[0], zero_stats) ) return data
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
def __init__(self, core): self.lock = Lock() self.core = core self.tasks = OrderedDict() #task store, for outgoing tasks only self.notifications = [] #list of notifications self.last_clients = { Output.Notification : 0, Output.Captcha : 0, Output.Query : 0, } self.ids = 0 #only for internal purpose
def get_system_info(): """ Returns system information as dict """ global info if info is None: import platform info = OrderedDict([ (_("Platform"), platform.platform()), (_("Version"), sys.version), (_("Path"), os.path.abspath("")), (_("Encoding"), sys.getdefaultencoding()), (_("FS-Encoding"), sys.getfilesystemencoding()) ]) return info
def __init__(self): """Constructor""" # core config sections from pyload self.baseSections = [] # Meta data information self.config = OrderedDict() # The actual config values self.values = {} self.changeCB = None # callback when config value was changed self.checkVersion() self.loadDefault() self.parseValues(self.CONFIG)
def getAllFiles(self, package=None, search=None, state=None, owner=None): """ Return dict with file information :param package: optional package to filter out :param search: or search string for file name :param unfinished: filter by dlstatus not finished :param owner: only specific owner """ qry = ( 'SELECT fid, name, owner, size, status, media, added, fileorder, ' 'url, plugin, hash, dlstatus, error, package FROM files WHERE ') arg = [] if state is not None and state != DS.All: qry += 'dlstatus IN (%s) AND ' % state_string(state) if owner is not None: qry += 'owner=? AND ' arg.append(owner) if package is not None: arg.append(package) qry += 'package=? AND ' if search is not None: search = "%%%s%%" % search.strip("%") arg.append(search) qry += "name LIKE ? " # make qry valid if qry.endswith("WHERE "): qry = qry[:-6] if qry.endswith("AND "): qry = qry[:-4] self.c.execute(qry + "ORDER BY package, fileorder", arg) data = OrderedDict() for r in self.c: f = FileInfo(r[0], r[1], r[13], r[2], r[3], r[4], r[5], r[6], r[7]) if r[11] > 0: # dl status != NA f.download = DownloadInfo(r[8], r[9], r[10], r[11], self.manager.statusMsg[r[11]], r[12]) data[r[0]] = f return data
def addConfigSection(self, section, name, desc, long_desc, 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) == 5: conf_name, type, conf_desc, conf_verbose, default = entry else: # config options without description conf_name, type, conf_desc, default = entry conf_verbose = "" d[conf_name] = ConfigData(gettext(conf_desc), type, gettext(conf_verbose), from_string(default, type)) data = SectionTuple(gettext(name), gettext(desc), gettext(long_desc), d) self.config[section] = data
class InteractionManager: """ Class that gives ability to interact with the user. Arbitrary tasks with predefined output and input types can be set off. Asynchronous callbacks and default values keep the ability to fallback if no user is present. """ # number of seconds a client is classified as active CLIENT_THRESHOLD = 60 def __init__(self, core): self.lock = Lock() self.core = core self.tasks = OrderedDict() #task store, for outgoing tasks only self.notifications = [] #list of notifications self.last_clients = { Output.Notification : 0, Output.Captcha : 0, Output.Query : 0, } self.ids = 0 #only for internal purpose def isClientConnected(self, mode=Output.All): if mode == Output.All: return max(self.last_clients.values()) + self.CLIENT_THRESHOLD <= time() else: self.last_clients.get(mode, 0) + self.CLIENT_THRESHOLD <= time() def updateClient(self, mode): t = time() for output in self.last_clients: if bits_set(output, mode): self.last_clients[output] = t @lock def work(self): # old notifications will be removed for n in [x for x in self.notifications if x.timedOut()]: self.notifications.remove(n) # store at most 100 notifications del self.notifications[50:] @lock def createNotification(self, title, content, desc="", plugin=""): """ 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, Input.Text, [content], Output.Notification, "", title, desc, plugin) self.ids += 1 self.notifications.insert(0, task) self.handleTask(task) return task @lock def newQueryTask(self, input, data, desc, default="", plugin=""): task = InteractionTask(self.ids, input, to_list(data), Output.Query, default, _("Query"), desc, plugin) self.ids += 1 return task @lock def newCaptchaTask(self, img, format, filename, plugin="", input=Input.Text): #todo: title desc plugin task = InteractionTask(self.ids, input, [img, format, filename],Output.Captcha, "", _("Captcha request"), _("Please solve the captcha."), plugin) self.ids += 1 return task @lock def removeTask(self, task): if task.iid in self.tasks: del self.tasks[task.iid] @lock def getTask(self, mode=Output.All): self.updateClient(mode) for task in self.tasks.itervalues(): if mode == Output.All or bits_set(task.output, mode): return task @lock def getNotifications(self): """retrieves notifications, old ones are only deleted after a while\ client has to make sure itself to dont display it twice""" for n in self.notifications: n.setWaiting(self.CLIENT_THRESHOLD * 5, True) #store notification for shorter period, lock the timeout return self.notifications def isTaskWaiting(self, mode=Output.All): return self.getTask(mode) is not None @lock def getTaskByID(self, iid): if iid in self.tasks: task = self.tasks[iid] del self.tasks[iid] return task def handleTask(self, task): cli = self.isClientConnected(task.output) if cli: #client connected -> should handle the task task.setWaiting(self.CLIENT_THRESHOLD) # wait for response if task.output == Output.Notification: task.setWaiting(60 * 60 * 30) # notifications are valid for 30h for plugin in self.core.addonManager.activePlugins(): try: plugin.newInteractionTask(task) except: self.core.print_exc() if task.output != Output.Notification: self.tasks[task.iid] = 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)
class InteractionManager: """ Class that gives ability to interact with the user. Arbitrary tasks with predefined output and input types can be set off. Asynchronous callbacks and default values keep the ability to fallback if no user is present. """ # number of seconds a client is classified as active CLIENT_THRESHOLD = 60 def __init__(self, core): self.lock = Lock() self.core = core self.tasks = OrderedDict() #task store, for outgoing tasks only self.notifications = [] #list of notifications self.last_clients = { Output.Notification: 0, Output.Captcha: 0, Output.Query: 0, } self.ids = 0 #only for internal purpose def isClientConnected(self, mode=Output.All): if mode == Output.All: return max( self.last_clients.values()) + self.CLIENT_THRESHOLD <= time() else: self.last_clients.get(mode, 0) + self.CLIENT_THRESHOLD <= time() def updateClient(self, mode): t = time() for output in self.last_clients: if bits_set(output, mode): self.last_clients[output] = t @lock def work(self): # old notifications will be removed for n in [x for x in self.notifications if x.timedOut()]: self.notifications.remove(n) # store at most 100 notifications del self.notifications[50:] @lock def createNotification(self, title, content, desc="", plugin=""): """ 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, Input.Text, [content], Output.Notification, "", title, desc, plugin) self.ids += 1 self.notifications.insert(0, task) self.handleTask(task) return task @lock def newQueryTask(self, input, data, desc, default="", plugin=""): task = InteractionTask(self.ids, input, to_list(data), Output.Query, default, _("Query"), desc, plugin) self.ids += 1 return task @lock def newCaptchaTask(self, img, format, filename, plugin="", input=Input.Text): #todo: title desc plugin task = InteractionTask(self.ids, input, [img, format, filename], Output.Captcha, "", _("Captcha request"), _("Please solve the captcha."), plugin) self.ids += 1 return task @lock def removeTask(self, task): if task.iid in self.tasks: del self.tasks[task.iid] @lock def getTask(self, mode=Output.All): self.updateClient(mode) for task in self.tasks.itervalues(): if mode == Output.All or bits_set(task.output, mode): return task @lock def getNotifications(self): """retrieves notifications, old ones are only deleted after a while\ client has to make sure itself to dont display it twice""" for n in self.notifications: n.setWaiting(self.CLIENT_THRESHOLD * 5, True) #store notification for shorter period, lock the timeout return self.notifications def isTaskWaiting(self, mode=Output.All): return self.getTask(mode) is not None @lock def getTaskByID(self, iid): if iid in self.tasks: task = self.tasks[iid] del self.tasks[iid] return task def handleTask(self, task): cli = self.isClientConnected(task.output) if cli: #client connected -> should handle the task task.setWaiting(self.CLIENT_THRESHOLD) # wait for response if task.output == Output.Notification: task.setWaiting(60 * 60 * 30) # notifications are valid for 30h for plugin in self.core.addonManager.activePlugins(): try: plugin.newInteractionTask(task) except: self.core.print_exc() if task.output != Output.Notification: self.tasks[task.iid] = task
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 {}
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 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
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
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)