Exemple #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)
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)
Exemple #3
0
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
Exemple #4
0
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