Ejemplo n.º 1
0
 def __init__(self, clicks_fn, keys_fn, screen_fn, system_fn, finale=None):
     """
     Initializes the needed parsers for the LogInfo class.
     """
     self.clicks_parser = click_parser.ClickParser(clicks_fn)
     self.keys_parser = key_parser.KeyParser(keys_fn)
     self.mixed_parser = mixed_parser.Parser(keys_fn, clicks_fn)
     self.clicks_filename = clicks_fn
     self.keys_filename = keys_fn
     self.screenshot_filename = screen_fn
     self.slog_filename = system_fn
     self.finale = finale
     self._pause_info = PauseInfo(self.mixed_parser)
     if self.finale is not None:
         # Phases analysis is only available if the finale file is given.
         try:
             self._pi = PhaseInfo(self.mixed_parser, self.slog_filename)
         except IndexError:
             pass
Ejemplo n.º 2
0
 def __init__(self, clicks_fn, keys_fn, screen_fn, system_fn, finale=None):
     """
     Initializes the needed parsers for the LogInfo class.
     """
     self.clicks_parser = click_parser.ClickParser(clicks_fn)
     self.keys_parser = key_parser.KeyParser(keys_fn)
     self.mixed_parser = mixed_parser.Parser(keys_fn, clicks_fn)
     self.clicks_filename = clicks_fn
     self.keys_filename = keys_fn
     self.screenshot_filename = screen_fn
     self.slog_filename = system_fn
     self.finale = finale
     self._pause_info = PauseInfo(self.mixed_parser)
     if self.finale is not None:
         # Phases analysis is only available if the finale file is given.
         try:
             self._pi = PhaseInfo(self.mixed_parser, self.slog_filename)
         except IndexError:
             pass
Ejemplo n.º 3
0
class LogInfo:
    """
    Presents the user with different types of summaries of the recorded logs.
    """
    def __init__(self, clicks_fn, keys_fn, screen_fn, system_fn, finale=None):
        """
        Initializes the needed parsers for the LogInfo class.
        """
        self.clicks_parser = click_parser.ClickParser(clicks_fn)
        self.keys_parser = key_parser.KeyParser(keys_fn)
        self.mixed_parser = mixed_parser.Parser(keys_fn, clicks_fn)
        self.clicks_filename = clicks_fn
        self.keys_filename = keys_fn
        self.screenshot_filename = screen_fn
        self.slog_filename = system_fn
        self.finale = finale
        self._pause_info = PauseInfo(self.mixed_parser)
        if self.finale is not None:
            # Phases analysis is only available if the finale file is given.
            try:
                self._pi = PhaseInfo(self.mixed_parser, self.slog_filename)
            except IndexError:
                pass

    def get_unique_pressed_clicks(self):
        """
        Returns a set with all the type of clicks that have been pressed
        in the translation session.
        """
        pressed_clicks = set({})
        for click in self.clicks_parser.clicks:
            date, time, pn, user, wid, title, ms, msg, x, y, res, img = click
            click_type, msg = msg.split('_')
            pressed_clicks.add(click_type)
        return pressed_clicks

    def get_all_pressed_clicks(self):
        """
            Returns a list with all the type of clicks and coordinates that
            have been pressed in  the translation sesion.
        """
        pressed_clicks = []
        for click in self.clicks_parser.clicks:
            date, time, pn, user, wid, title, ms, msg, x, y, res, img = click
            click_type, msg = msg.split('_')
            pressed_clicks += [(x, y, res, click_type, msg, pn, title)]
        return pressed_clicks

    def get_click_info(self):
        """
            Given a structure containing the parsed log, it creates a list of
        tuples where each row contains the following information:
            TYPE OF CLICK - TIME PRESS - RELEASE TIME - TOTAL PRESS TIME -
            PRESS MOUSE X - PRESS MOUSE Y - RELEASE MOUSE X - RELEASE MOUSE Y
        """
        # pn - program name
        # wid - window_id
        # x - coordinate x
        # y - coordinate y
        # ms - millisecond
        # pt - press time
        # rt - release time
        # tt - total time
        # dx - down x
        # ux - up x
        result = []  # List of tuples
        pc = {}  # Pending_clicks
        for click in self.clicks_parser.clicks:
            date, time, pn, user, wid, title, ms, msg, x, y, res, img = click
            #print msg
            ct, msg = msg.split('_')  # click_type and message
            if msg == DOWN:
                if ct not in pc:
                    # Fills the list with the partial information.
                    pc[ct] = [ct, [ms], None, None, [x], [y], None, None]
                else:
                    # More than one down.
                    ct, pt, rt, tt, dx, dx, ux, ux = pc.pop(ct)
                    pc[ct] = [
                        ct, pt + [ms], None, None, dx + [x], dx + [y], None,
                        None
                    ]
            else:  # It's an UP msg
                #print ct
                if ct not in pc:
                    msg = "Ha ocurrido un error fatal. "
                    msg += "El log no tiene el formato deseado."
                    print msg
                    exit(1)
                else:
                    ct, pt, rt, tt, dx, dy, ux, uy = pc.pop(ct)
                    result += [(ct, pt, ms, int(ms) - int(pt[0]), dx, dy, x, y)
                               ]
        return result

    def print_click_summary(self):
        """
        Prints a summary of the click activity in the session.
        """
        # ct - click_type
        # pn - program_name
        click_amount = 0
        windows = set([])
        for click in self.get_all_pressed_clicks():
            x, y, resolution, ct, msg, pn, title = click
            if msg == 'down':
                click_amount += 1
            windows.add(pn)
        esp = 0
        var = 0
        for click in self.get_click_info():
            click_type, pt, rt, tt, dx, dy, ux, uy = click
            esp += tt
            var += tt * tt
            #print tt
        esp = esp / float(click_amount)
        var = (var / float(click_amount)) - (esp * esp)
        # Prints results.
        """print "*** RESUMEN DE CLICKS ***"
        print "Cantidad total de clicks:", colored(click_amount, 'blue')
        print "Ventanas en las cuales se hacen clicks:", list(windows)
        print "Tipos de clicks usados:", list(self.get_unique_pressed_clicks())
        print "Tiempo de presión de click promedio:", colored(esp, 'blue')
        print "Varianza en la presión de click:", colored(var, 'blue')
        print "Desviación estándar en la presión de click:", colored(sqrt(var), 'blue')"""

        #return results
        return [
            click_amount,
            list(windows),
            list(self.get_unique_pressed_clicks()), esp, var,
            sqrt(var)
        ]

    def get_unique_pressed_keys(self):
        """
            Returns a set with all the letters that have been pressed during
        the translation session.
        """
        # pn - program_name
        # wid - windows_id
        # ms - milliseconds
        pressed_keys = set({})
        for key in self.keys_parser.keys:
            # Get attributes from the log line.
            date, time, pn, user, wid, title, ms, key_id, msg, x, y = key
            pressed_keys.add(key_id)
        if '#' in pressed_keys:
            pressed_keys.remove('#')
        return pressed_keys

    def get__milliseconds_delta(self, milliseconds):
        '''
        By getting the minimum timestamp it is then possible
        to infer the relation between the timestamps, where the first
        timestamp will be zero and the other ones after it will
        increase by the time passed in between.
        '''
        if not hasattr(self, 'first_milliseconds_timestamp'):
            self.first_milliseconds_timestamp = \
                int(min(min(self.mixed_parser.clicks, key=lambda x: x[6]),min(self.mixed_parser.keys, key=lambda x: x[6]))[6])
        return milliseconds - self.first_milliseconds_timestamp

    def get_date_from_mixedlog_format(self, click_or_key_log):
        '''
        By using the format from mixedlog, where
        click_or_key_log[0] is the date in the format %Y%M, example 199912
        click_or_key_log[1] is the hour/minute in the format %H%M, 1959
        click_or_key_log[6] is the milliseconds that represent the uptime of the machine
        it is possible to combine them into a complete date
        '''
        from_milliseconds = self.get__milliseconds_delta(
            int(click_or_key_log[6])) / 1000.0
        from_milliseconds = datetime.datetime.fromtimestamp(
            from_milliseconds).second
        datehour = click_or_key_log[0] + ' ' + click_or_key_log[1] + str(
            from_milliseconds)
        date = datetime.datetime.strptime(datehour, "%Y%m%d %H%M%S")
        return date

    def get_all_pressed_keys(self):
        """
            Returns a list with all the keys that had been pressed with
        corresponding up and downs messages.

            It can contain repeated elements because every key can have several
        downs and at least one up event.
        """
        pressed_keys = []
        for key in self.keys_parser.keys:
            # Get attributes from the log line.
            date, time, pn, user, wid, title, ms, key_id, msg, x, y = key
            key_type, msg = msg.split("_")
            pressed_keys.append((key_id, msg, pn, title))
        return pressed_keys

    def get_letter_info(self):
        """
            Given a structure containing the parsed log, it creates a list of
        tuples where each row contains the following information:
            KEY - TIME PRESS - RELEASE TIME - TOTAL PRESS TIME -
            PRESS MOUSE X - PRESS MOUSE Y - RELEASE MOUSE X - RELEASE MOUSE Y
        """
        # pk - pending_keys
        # pn - program_name
        # kd - key_dict
        result = []  # List of tuples
        # Create keySet
        kd = dict([])
        keywds = []
        for key_struct in self.keys_parser.keys:
            date, time, pn, user, wid, title, ms, key, msg, x, y = key_struct
            #print date, time, pn, user, wid, title, ms, key, msg, x, y
            keywds += [ms]
            kd[ms] = key_struct
        pk = {}
        for kwd in keywds:
            #print kd[kwd]
            date, time, pn, user, wid, title, ms, key, msg, x, y = kd[kwd]
            if msg == KDOWN:
                if key not in pk:
                    # Fills the list with the partial information.
                    pk[key] = \
                        [key, [ms], None, None, [x], [y], None, None]
                else:
                    # More than one down.
                    key, pt, rt, tt, dx, dy, ux, uy = pk.pop(key)
                    pk[key] = [
                        key, pt + [ms], None, None, dx + [x], dy + [y], None,
                        None
                    ]
            else:  # It's an UP msg
                if key not in pk:
                    msg = "Ha ocurrido un error fatal. "
                    msg += "El log keys no tiene el formato deseado."
                    print msg
                    exit(1)
                else:
                    key, pt, rt, tt, dx, dy, ux, uy = pk.pop(key)
                    result += [(key, pt, ms, int(ms) - int(pt[0]), dx, dy, x,
                                y)]
        return result

    def _get_combos(self):
        """
        Returns the list of combos that were used in the session.
        """
        combos = []
        # CTRL + KEY combos
        # Eg. CTRL + V, CTRL + C, etc
        combo = []
        combing = False
        found_key = None
        SPECIAL = {'ctrll', 'ctrlr', 'altl', 'altr'}
        for key in self.keys_parser.keys:
            date, time, pn, user, wid, title, ms, keystroke, msg, x, y = key
            if msg == 'key_down' and keystroke in SPECIAL and not combing:
                combo += [keystroke]
                found_key = keystroke
                combing = True
            elif msg == 'key_down' and combing:
                combo += [keystroke]
            elif msg == 'key_up' and combing and keystroke == found_key:
                if len(combo) > 1:
                    combos += [combo]
                combing = False
                found_key = None
                combo = []

        return combos

    def print_key_summary(self):
        """
        Prints a complete summary on the key log file.
        """

        key_amount = 0
        function_keys = set([])
        move_keys = set([])
        erase_keys = set([])
        combos = self._get_combos()
        windows = set([])
        for k in self.keys_parser.keys:
            date, time, pn, user, wid, title, ms, key, msg, x, y = k
            #print date, time, pn, user, wid, title, ms, key, msg, x, y
            if msg == "key_down":
                key_amount += 1
            windows.add(pn)
            if key in FUNCTION_KEYS:
                function_keys.add(key)
            elif key in ERASE_KEYS:
                erase_keys.add(key)
            elif key in MOVE_KEYS:
                move_keys.add(key)
        esp = 0
        var = 0

        for k in self.get_letter_info():
            key, dt, ut, tt, dx, dy, ux, uy = k
            #print key, dt, ut, tt, dx, dy, ux, uy
            esp += tt
            var += tt * tt
        esp = esp / float(key_amount)
        var = (var / float(key_amount)) - (esp * esp)
        # Print results
        """print "*** RESUMEN DE TECLAS ***"
        print "Cantidad de teclas presionadas:", colored(key_amount, 'blue')
        print "Teclas únicas presionadas:", list(self.get_unique_pressed_keys())
        print "Ventanas donde se esribió:", list(windows)
        print "Tiempo de presión de tecla promedio:", colored(esp, 'blue')
        print "Varianza de presión de tecla:", colored(var, 'blue')
        print "DE de presión de tecla:", colored(sqrt(var), 'blue')
        print "Teclas de función usadas:", list(function_keys)
        print "Patrones de borrado usados:", list(erase_keys)
        print "Teclas de movimiento usadas:", list(move_keys)
        print "Combos de teclas usados:", list(combos)"""

        #return results
        return [
            key_amount,
            list(self.get_unique_pressed_keys()),
            list(windows), esp, var,
            sqrt(var),
            list(function_keys),
            list(erase_keys),
            list(move_keys),
            list(combos)
        ]

    def get_orientation_info(self):
        """
        Returns a pair (begin, end) of the time of the orientation phase.
        """
        return self._pi.get_orientation_info()

    def get_drafting_info(self):
        """
        Returns a pair (begin, end) of the time of the drafting phase.
        """
        return self._pi.get_drafting_info()

    def get_revision_info(self):
        """
        Returns a pair (begin, end) of the time of the revision phase.
        """
        return self._pi.get_revision_info()

    def get_total_session_time(self):
        """
        Returns the total session time.
        """
        return self._pi.get_total_session_time()

    def print_phases_summary(self):
        """
        Prints a summary on the translation phases: duration, percentage of the
        sesion, etc.
        """
        print "*** RESUMEN DE FASES ***"
        self._pi.print_orientation_info()
        self._pi.print_drafting_info()
        self._pi.print_revision_info()
        self._pi.print_total_session_info()

    def print_pauses(self, begin, end):
        """
        Prints a summary on the pauses.
        """
        self._pause_info.print_pauses(begin, end)

    def get_time_by_active_window(self):
        """
        TODO: Change this comment so that it looks like the others
        By using the knowledge that immediately after a click or a key is down inside a program
        then that program is the active program, then the time between that event and the next
        can be assigned as time used inside that program, the active one.
        Doing this for all of the recorded events gives back the amount of time spent on each
        active program, with no superposition in between them whatsoever.
        """
        time_by_active_window = {}
        time_by_active_window["total"] = 0
        for i in range(len(self.mixed_parser.keys) - 1):
            # Get current keystroke.
            cdate, ctime, cprogram_name, cusername, cwindow_id, cwindow_title, cmiliseconds, ckey, cmsg, cxcoord, cycoord = self.mixed_parser.keys[
                i]
            # Get next keystroke.
            ndate, ntime, nprogram_name, nusername, nwindow_id, nwindow_title, nmiliseconds, nkey, nmsg, nxcoord, nycoord = self.mixed_parser.keys[
                i + 1]

            delta = int(nmiliseconds) - int(cmiliseconds)
            if cprogram_name in time_by_active_window:
                time_by_active_window[cprogram_name] += delta
            else:
                time_by_active_window[cprogram_name] = delta

            time_by_active_window["total"] += delta
        return time_by_active_window

    def plot_window_distribution_pie_chart(self):
        """
        Plots a pie chart of how the windows used in the session.
        """
        # Get data
        win_distrib = self.get_time_by_active_window()
        del win_distrib['total']
        keys = win_distrib.keys()
        values = win_distrib.values()

        # Pick colors
        start_color = Color("#CCE5FF")
        colors = map(convert_to_hex,
                     list(start_color.range_to(Color("#003366"), len(keys))))

        # Plot pie chart
        fig, ax = plt.subplots(figsize=(12, 8))
        ax.pie(values,
               autopct='%1.0f%%',
               pctdistance=1.1,
               labeldistance=0.5,
               shadow=True,
               startangle=10,
               colors=colors)
        ax.set_title("Pie chart: Time spent by window")
        plt.legend(keys, loc="best", shadow=True)
        plt.show()

    # def get_pauses_by_phase(self, phase):
    #     """
    #     Returns a list with all the pauses in:
    #     - phase == 1: the orientation phase.
    #     - phase == 2: the drafting phase.
    #     - phase == 3: the revision phase.
    #     """
    #     if phase == 1:
    #         begin, end = self._pi.get_orientation_info()
    #         return self._pause_info.get_pauses(begin, end)
    #     elif phase == 2:
    #         begin, end = self._pi.get_drafting_info()
    #         return self._pause_info.get_pauses(begin, end)
    #     elif phase == 3:
    #         begin, end = self._pi.get_revision_info()
    #         return self._pause_info.get_pauses(begin, end)
    #     else:
    #         return None

    def print_pause_summary(self, begin, end):
        """
        Prints a summary of the pauses in the session.
        """
        return self._pause_info.print_pause_summary(begin, end)

    def plot_keystroke_progression_graph(self, bin_size):
        """
        Plots a keystroke progression graph.
        @bin_size is the bin size in seconds
        """
        last_bin_size = bin_size * 1000  # Convert to milliseconds
        current_bin, current_bin_size = 0, 0
        keystrokes = defaultdict(int)
        current_keystrokes, i = 0, 0
        start = int(self.keys_parser.keys[0][6])  # ms
        while i < len(self.keys_parser.keys):
            d, t, pn, user, wid, title, ms, key_id, msg, x, y = self.keys_parser.keys[
                i]
            ms = int(ms) - start  # force to start from ms 0
            if current_bin_size + ms <= last_bin_size:
                if "down" in msg:
                    current_bin_size += ms
                    current_keystrokes += 1
            else:
                current_bin_size = 0
                if "down" in msg:
                    current_keystrokes += 1
                    current_bin_size += ms
                last_bin_size *= 2
                keystrokes[current_bin] = current_keystrokes
                current_bin += 1
            i += 1
        fig, ax = plt.subplots(figsize=(12, 8))
        ax.plot(keystrokes.keys(), keystrokes.values(), 'r', color='#4682b4')
        ax.plot(keystrokes.keys(), keystrokes.values(), 'o', color='#19457e')
        ax.set_xlabel('# Bins of %s milliseconds' % str(bin_size * 1000))
        ax.set_ylabel('# Keystrokes')
        ax.set_title("Keystroke progression graph")
        ax.plot()
        plt.show()

    def plot_clicks_progression_graph(self, bin_size):
        """
        Plots a clicks progression graph.
        @bin_size is the bin size in seconds
        """
        last_bin_size = bin_size * 1000  # Convert to milliseconds
        current_bin, current_bin_size = 0, 0
        clicks = defaultdict(int)
        current_clicks, i = 0, 0
        start = int(self.clicks_parser.clicks[0][6])  # ms
        while i < len(self.clicks_parser.clicks):
            date, time, pn, user, wid, title, ms, msg, x, y, res, img = self.clicks_parser.clicks[
                i]
            ms = int(ms) - start  # force to start from ms 0
            if current_bin_size + ms <= last_bin_size:
                if "down" in msg:
                    current_bin_size += ms
                    current_clicks += 1
            else:
                current_bin_size = 0
                if "down" in msg:
                    current_clicks += 1
                    current_bin_size += ms
                last_bin_size *= 2
                clicks[current_bin] = current_clicks
                current_bin += 1
            i += 1
        fig, ax = plt.subplots(figsize=(12, 8))
        ax.plot(clicks.keys(), clicks.values(), 'r', color='#4682b4')
        ax.plot(clicks.keys(), clicks.values(), 'o', color='#19457e')
        ax.set_xlabel('# Bins of %s milliseconds' % str(bin_size * 1000))
        ax.set_ylabel('# Clicks pressed')
        ax.set_title("Clicks progression graph")
        plt.show()

    def mark_click(self, x, y, r=0, b=0, g=0):
        """
        @brief      Marks a pixel and neighbors in an image.
        """
        if x - 1 >= 0:
            if y - 1 >= 0:
                self.pixels[x - 1, y - 1] = (r, b, g)
            self.pixels[x - 1, y] = (r, b, g)
            if y + 1 < self.img_height:
                self.pixels[x - 1, y + 1] = (r, b, g)
        if y - 1 >= 0:
            self.pixels[x, y - 1] = (r, b, g)
        self.pixels[x, y] = (r, b, g)
        if y + 1 < self.img_height:
            self.pixels[x, y + 1] = (r, b, g)
        if x + 1 < self.img_width:
            if y - 1 >= 0:
                self.pixels[x + 1, y - 1] = (r, b, g)
            self.pixels[x + 1, y] = (r, b, g)
            if y + 1 < self.img_height:
                self.pixels[x + 1, y + 1] = (r, b, g)

    def plot_clicks_in_screenshot(self, screenshot):
        """
        @brief      Marks in a screenshot where clicks were made.

        @param      screenshot  The screenshot path
        """
        img = Image.open(screenshot)
        self.pixels = img.load()
        self.img_width, self.img_height = img.size
        # Get clicks (down only)
        clicks = self.get_click_info()
        # Define color
        r, g, b = 40, 205, 106
        # For each click, mark the surrounding area
        for click in clicks:
            _, _, _, _, _, _, x, y = click
            self.mark_click(int(x), int(y), r, g, b)
        img.show()

    # Funciones agregadas

    def get_mixed_parser(self):
        return self.mixed_parser
Ejemplo n.º 4
0
class LogInfo:
    """
    Presents the user with different types of summaries of the recorded logs.
    """
    def __init__(self, clicks_fn, keys_fn, screen_fn, system_fn, finale=None):
        """
        Initializes the needed parsers for the LogInfo class.
        """
        self.clicks_parser = click_parser.ClickParser(clicks_fn)
        self.keys_parser = key_parser.KeyParser(keys_fn)
        self.mixed_parser = mixed_parser.Parser(keys_fn, clicks_fn)
        self.clicks_filename = clicks_fn
        self.keys_filename = keys_fn
        self.screenshot_filename = screen_fn
        self.slog_filename = system_fn
        self.finale = finale
        self._pause_info = PauseInfo(self.mixed_parser)
        if self.finale is not None:
            # Phases analysis is only available if the finale file is given.
            try:
                self._pi = PhaseInfo(self.mixed_parser, self.slog_filename)
            except IndexError:
                pass

    def get_unique_pressed_clicks(self):
        """
            Returns a set with all the type of clicks that have been pressed
        in the translation session.
        """
        pressed_clicks = set({})
        for click in self.clicks_parser.clicks:
            date, time, pn, user, wid, title, ms, msg, x, y, res, img = click
            click_type, msg = msg.split('_')
            pressed_clicks.add(click_type)
        return pressed_clicks

    def get_all_pressed_clicks(self):
        """
            Returns a list with all the type of clicks and coordinates that
            have been pressed in  the translation sesion.
        """
        pressed_clicks = []
        for click in self.clicks_parser.clicks:
            date, time, pn, user, wid, title, ms, msg, x, y, res, img = click
            click_type, msg = msg.split('_')
            pressed_clicks += [(x, y, res, click_type, msg, pn, title)]
        return pressed_clicks

    def get_click_info(self):
        """
            Given a structure containing the parsed log, it creates a list of
        tuples where each row contains the following information:
            TYPE OF CLICK - TIME PRESS - RELEASE TIME - TOTAL PRESS TIME -
            PRESS MOUSE X - PRESS MOUSE Y - RELEASE MOUSE X - RELEASE MOUSE Y
        """
        # pn - program name
        # wid - window_id
        # x - coordinate x
        # y - coordinate y
        # ms - millisecond
        # pt - press time
        # rt - release time
        # tt - total time
        # dx - down x
        # ux - up x
        result = []  # List of tuples
        pc = {}  # Pending_clicks
        for click in self.clicks_parser.clicks:
            date, time, pn, user, wid, title, ms, msg, x, y, res, img = click
            ct, msg = msg.split('_')  # click_type and message
            if msg == DOWN:
                if ct not in pc:
                    # Fills the list with the partial information.
                    pc[ct] = [ct, [ms], None, None, [x], [y], None, None]
                else:
                    # More than one down.
                    ct, pt, rt, tt, dx, dx, ux, ux = pc.pop(ct)
                    pc[ct] = [ct, pt+[ms], None, None, dx+[x], dx+[y], None, None]
            else:  # It's an UP msg
                if ct not in pc:
                    msg = "Ha ocurrido un error fatal. "
                    msg += "El log no tiene el formato deseado."
                    print msg
                    exit(1)
                else:
                    ct, pt, rt, tt, dx, dy, ux, uy = pc.pop(ct)
                    result += [(ct, pt, ms, int(ms)-int(pt[0]), dx, dy, x, y)]
        return result

    def print_click_summary(self):
        """
        Prints a summary of the click activity in the session.
        """
        # ct - click_type
        # pn - program_name
        click_amount = 0
        windows = set([])
        for click in self.get_all_pressed_clicks():
            x, y, resolution, ct, msg, pn, title = click
            if msg == 'down':
                click_amount += 1
            windows.add(pn)
        esp = 0
        var = 0
        for click in self.get_click_info():
            click_type, pt, rt, tt, dx, dy, ux, uy = click
            esp += tt
            var += tt * tt
            print tt
        esp = esp / float(click_amount)
        var = (var/float(click_amount))-(esp*esp)
        # Prints results.
        print "*** RESUMEN DE CLICKS ***"
        print "Cantidad total de clicks:", colored(click_amount, 'blue')
        print "Ventanas en las cuales se hacen clicks:", list(windows)
        print "Tipos de clicks usados:", list(self.get_unique_pressed_clicks())
        print "Tiempo de presión de click promedio:", colored(esp, 'blue')
        print "Varianza en la presión de click:", colored(var, 'blue')
        print "Desviación estándar en la presión de click:", colored(sqrt(var), 'blue')

    def get_unique_pressed_keys(self):
        """
            Returns a set with all the letters that have been pressed during
        the translation session.
        """
        # pn - program_name
        # wid - windows_id
        # ms - milliseconds
        pressed_keys = set({})
        for key in self.keys_parser.keys:
            # Get attributes from the log line.
            date, time, pn, user, wid, title, ms, key_id, msg, x, y = key
            pressed_keys.add(key_id)
        if '#' in pressed_keys:
            pressed_keys.remove('#')
        return pressed_keys

    def get_all_pressed_keys(self):
        """
            Returns a list with all the keys that had been pressed with
        corresponding up and downs messages.

            It can contain repeated elements because every key can have several
        downs and at least one up event.
        """
        pressed_keys = []
        for key in self.keys_parser.keys:
            # Get attributes from the log line.
            date, time, pn, user, wid, title, ms, key_id, msg, x, y = key
            key_type, msg = msg.split("_")
            pressed_keys.append((key_id, msg, pn, title))
        return pressed_keys

    def get_letter_info(self):
        """
            Given a structure containing the parsed log, it creates a list of
        tuples where each row contains the following information:
            KEY - TIME PRESS - RELEASE TIME - TOTAL PRESS TIME -
            PRESS MOUSE X - PRESS MOUSE Y - RELEASE MOUSE X - RELEASE MOUSE Y
        """
        # pk - pending_keys
        # pn - program_name
        # kd - key_dict
        result = []  # List of tuples
        # Create keySet
        kd = dict([])
        keywds = []
        for key_struct in self.keys_parser.keys:
            date, time, pn, user, wid, title, ms, key, msg, x, y = key_struct
            keywds += [ms]
            kd[ms] = key_struct
        pk = {}
        for kwd in keywds:
            date, time, pn, user, wid, title, ms, key, msg, x, y = kd[kwd]
            if msg == KDOWN:
                if key not in pk:
                    # Fills the list with the partial information.
                    pk[key] = \
                        [key, [ms], None, None, [x], [y], None, None]
                else:
                    # More than one down.
                    key, pt, rt, tt, dx, dy, ux, uy = pk.pop(key)
                    pk[key] = [key, pt+[ms], None, None, dx+[x], dy+[y], None, None]
            else:  # It's an UP msg
                if key not in pk:
                    msg = "Ha ocurrido un error fatal. "
                    msg += "El log no tiene el formato deseado."
                    print msg
                    exit(1)
                else:
                    key, pt, rt, tt, dx, dy, ux, uy = pk.pop(key)
                    result += [(key, pt, ms, int(ms)-int(pt[0]), dx, dy, x, y)]
        return result

    def _get_combos(self):
        """
        Returns the list of combos that were used in the session.
        """
        combos = []
        # CTRL + KEY combos
        # Eg. CTRL + V, CTRL + C, etc
        combo = []
        combing = False
        found_key = None
        SPECIAL = {'ctrll', 'ctrlr', 'altl', 'altr'}
        for key in self.keys_parser.keys:
            date, time, pn, user, wid, title, ms, keystroke, msg, x, y = key
            if msg == 'key_down' and keystroke in SPECIAL and not combing:
                combo += [keystroke]
                found_key = keystroke
                combing = True
            elif msg == 'key_down' and combing:
                combo += [keystroke]
            elif msg == 'key_up' and combing and keystroke == found_key:
                if len(combo) > 1:
                    combos += [combo]
                combing = False
                found_key = None
                combo = []

        return combos

    def print_key_summary(self):
        """
        Prints a complete summary on the key log file.
        """
        key_amount = 0
        function_keys = set([])
        move_keys = set([])
        erase_keys = set([])
        combos = self._get_combos()
        windows = set([])
        for k in self.keys_parser.keys:
            date, time, pn, user, wid, title, ms, key, msg, x, y = k
            if msg == "key_down":
                key_amount += 1
            windows.add(pn)
            if key in FUNCTION_KEYS:
                function_keys.add(key)
            elif key in ERASE_KEYS:
                erase_keys.add(key)
            elif key in MOVE_KEYS:
                move_keys.add(key)
        esp = 0
        var = 0
        for k in self.get_letter_info():
            key, dt, ut, tt, dx, dy, ux, uy = k
            esp += tt
            var += tt * tt
        esp = esp / float(key_amount)
        var = (var / float(key_amount)) - (esp * esp)
        # Print results
        print "*** RESUMEN DE TECLAS ***"
        print "Cantidad de teclas presionadas:", colored(key_amount, 'blue')
        print "Teclas únicas presionadas:", list(self.get_unique_pressed_keys())
        print "Ventanas donde se esribió:", list(windows)
        print "Tiempo de presión de tecla promedio:", colored(esp, 'blue')
        print "Varianza de presión de tecla:", colored(var, 'blue')
        print "DE de presión de tecla:", colored(sqrt(var), 'blue')
        print "Teclas de función usadas:", list(function_keys)
        print "Patrones de borrado usados:", list(erase_keys)
        print "Teclas de movimiento usadas:", list(move_keys)
        print "Combos de teclas usados:", list(combos)

    def get_orientation_info(self):
        """
        Returns a pair (begin, end) of the time of the orientation phase.
        """
        return self._pi.get_orientation_info()

    def get_drafting_info(self):
        """
        Returns a pair (begin, end) of the time of the drafting phase.
        """
        return self._pi.get_drafting_info()

    def get_revision_info(self):
        """
        Returns a pair (begin, end) of the time of the revision phase.
        """
        return self._pi.get_revision_info()

    def get_total_session_time(self):
        """
        Returns the total session time.
        """
        return self._pi.get_total_session_time()

    def print_phases_summary(self):
        """
        Prints a summary on the translation phases: duration, percentage of the
        sesion, etc.
        """
        print "*** RESUMEN DE FASES ***"
        self._pi.print_orientation_info()
        self._pi.print_drafting_info()
        self._pi.print_revision_info()
        self._pi.print_total_session_info()

    def print_pauses(self, begin, end):
        """
        Prints a summary on the pauses.
        """
        self._pause_info.print_pauses(begin, end)

    def get_time_by_active_window(self):
        """
        TODO: Change this comment so that it looks like the others
        By using the knowledge that immediately after a click or a key is down inside a program
        then that program is the active program, then the time between that event and the next
        can be assigned as time used inside that program, the active one.
        Doing this for all of the recorded events gives back the amount of time spent on each
        active program, with no superposition in between them whatsoever.
        """
        time_by_active_window = {}
        time_by_active_window["total"] = 0
        for i in range(len(self.mixed_parser.keys)-1):
            # Get current keystroke.
            cdate, ctime, cprogram_name, cusername, cwindow_id, cwindow_title, cmiliseconds, ckey, cmsg, cxcoord, cycoord = self.mixed_parser.keys[i]
            # Get next keystroke.
            ndate, ntime, nprogram_name, nusername, nwindow_id, nwindow_title, nmiliseconds, nkey, nmsg, nxcoord, nycoord = self.mixed_parser.keys[i+1]

            delta = int(nmiliseconds)-int(cmiliseconds)
            if cprogram_name in time_by_active_window:
                time_by_active_window[cprogram_name] += delta
            else:
                time_by_active_window[cprogram_name] = delta

            time_by_active_window["total"] += delta
        return time_by_active_window

    def plot_window_distribution_pie_chart(self):
        """
        Plots a pie chart of how the windows used in the session.
        """
        # Get data
        win_distrib = self.get_time_by_active_window()
        del win_distrib['total']
        keys = win_distrib.keys()
        values = win_distrib.values()

        # Pick colors
        start_color = Color("#CCE5FF")
        colors = map(convert_to_hex, list(start_color.range_to(Color("#003366"), len(keys))))

        # Plot pie chart
        fig, ax = plt.subplots(figsize=(12,8))
        ax.pie(values,
               autopct='%1.0f%%',
               pctdistance=1.1,
               labeldistance=0.5,
               shadow=True,
               startangle=10,
               colors=colors)
        ax.set_title("Pie chart: Time spent by window")
        plt.legend(keys, loc="best",shadow=True)
        plt.show()

    # def get_pauses_by_phase(self, phase):
    #     """
    #     Returns a list with all the pauses in:
    #     - phase == 1: the orientation phase.
    #     - phase == 2: the drafting phase.
    #     - phase == 3: the revision phase.
    #     """
    #     if phase == 1:
    #         begin, end = self._pi.get_orientation_info()
    #         return self._pause_info.get_pauses(begin, end)
    #     elif phase == 2:
    #         begin, end = self._pi.get_drafting_info()
    #         return self._pause_info.get_pauses(begin, end)
    #     elif phase == 3:
    #         begin, end = self._pi.get_revision_info()
    #         return self._pause_info.get_pauses(begin, end)
    #     else:
    #         return None

    def print_pause_summary(self, begin, end):
        """
        Prints a summary of the pauses in the session.
        """
        return self._pause_info.print_pause_summary(begin, end)

    def plot_keystroke_progression_graph(self, bin_size):
        """
        Plots a keystroke progression graph.
        @bin_size is the bin size in seconds
        """
        last_bin_size = bin_size * 1000  # Convert to milliseconds
        current_bin, current_bin_size = 0, 0
        keystrokes = defaultdict(int)
        current_keystrokes, i = 0, 0
        start = int(self.keys_parser.keys[0][6])  # ms
        while i < len(self.keys_parser.keys):
            d, t, pn, user, wid, title, ms, key_id, msg, x, y = self.keys_parser.keys[i]
            ms = int(ms) - start  # force to start from ms 0
            if current_bin_size + ms <= last_bin_size:
                if "down" in msg:
                    current_bin_size += ms
                    current_keystrokes += 1
            else:
                current_bin_size = 0
                if "down" in msg:
                    current_keystrokes += 1
                    current_bin_size += ms
                last_bin_size *= 2
                keystrokes[current_bin] = current_keystrokes
                current_bin += 1
            i += 1
        fig, ax = plt.subplots(figsize=(12,8))
        ax.plot(keystrokes.keys(), keystrokes.values(), 'r', color='#4682b4')
        ax.plot(keystrokes.keys(), keystrokes.values(), 'o', color='#19457e')
        ax.set_xlabel('# Bins of %s milliseconds' % str(bin_size * 1000))
        ax.set_ylabel('# Keystrokes')
        ax.set_title("Keystroke progression graph")
        ax.plot()
        plt.show()

    def plot_clicks_progression_graph(self, bin_size):
        """
        Plots a clicks progression graph.
        @bin_size is the bin size in seconds
        """
        last_bin_size = bin_size * 1000  # Convert to milliseconds
        current_bin, current_bin_size = 0, 0
        clicks = defaultdict(int)
        current_clicks, i = 0, 0
        start = int(self.clicks_parser.clicks[0][6])  # ms
        while i < len(self.clicks_parser.clicks):
            date, time, pn, user, wid, title, ms, msg, x, y, res, img = self.clicks_parser.clicks[i]
            ms = int(ms) - start  # force to start from ms 0
            if current_bin_size + ms <= last_bin_size:
                if "down" in msg:
                    current_bin_size += ms
                    current_clicks += 1
            else:
                current_bin_size = 0
                if "down" in msg:
                    current_clicks += 1
                    current_bin_size += ms
                last_bin_size *= 2
                clicks[current_bin] = current_clicks
                current_bin += 1
            i += 1
        fig, ax = plt.subplots(figsize=(12,8))
        ax.plot(clicks.keys(), clicks.values(), 'r', color='#4682b4')
        ax.plot(clicks.keys(), clicks.values(), 'o', color='#19457e')
        ax.set_xlabel('# Bins of %s milliseconds' % str(bin_size * 1000))
        ax.set_ylabel('# Clicks pressed')
        ax.set_title("Clicks progression graph")
        plt.show()

    def mark_click(self, x, y, r=0, b=0, g=0):
        """
        @brief      Marks a pixel and neighbors in an image.
        """
        if x - 1 >= 0:
            if y - 1 >= 0:
                self.pixels[x-1, y-1] = (r, b, g)
            self.pixels[x-1, y] = (r, b, g)
            if y + 1 < self.img_height:
                self.pixels[x-1, y+1] = (r, b, g)
        if y - 1 >= 0:
            self.pixels[x, y-1] = (r, b, g)
        self.pixels[x, y] = (r, b, g)
        if y + 1 < self.img_height:
            self.pixels[x, y+1] = (r, b, g)
        if x + 1 < self.img_width:
            if y - 1 >= 0:
                self.pixels[x+1, y-1] = (r, b, g)
            self.pixels[x+1, y] = (r, b, g)
            if y + 1 < self.img_height:
                self.pixels[x+1, y+1] = (r, b, g)

    def plot_clicks_in_screenshot(self, screenshot):
        """
        @brief      Marks in a screenshot where clicks were made.

        @param      screenshot  The screenshot path
        """
        img = Image.open(screenshot)
        self.pixels = img.load()
        self.img_width = img.width
        self.img_height = img.height
        # Get clicks (down only)
        clicks = self.get_click_info()
        # Define color
        r, g, b = 40, 205, 106
        # For each click, mark the surrounding area
        for click in clicks:
            _ , _, _, _, _, _, x, y = click
            self.mark_click(int(x), int(y), r, g, b)
        img.show()