hSaria/cPing

View on GitHub
cping/layouts/legacy.py

Summary

Maintainability
A
0 mins
Test Coverage
'''A line-based, non-interactive layout. The "original".'''
import shutil
import time

import cping.layouts

HISTOGRAM_LENGTH_MINIMUM = 5


class Layout(cping.layouts.Layout):
    '''A line-based, non-interactive layout. The "original".'''

    def __call__(self):
        try:
            # Enable alternate screen buffer
            print('\x1b[?1049h', end='')

            # pylint: disable=not-an-iterable  # linter bug
            while any(host.is_running() for host in self.hosts):
                # Move to top left, then print table
                print(f'\x1b[H{get_table(self.hosts)}', end='', flush=True)
                time.sleep(self.protocol.interval)
        finally:
            # Disable alternate screen buffer
            print('\x1b[?1049l', end='')

            # Print the last summary, including any overflowing hosts
            print(get_table(self.hosts, all_hosts=True), end='')


def format_host(host, host_padding, line_width):
    '''Returns a line representing a summary of the host's results.'''
    # line_width - host - min (8) - avg (8) - max (8) - stdev (8) - loss (8)
    host.set_results_length(line_width - host_padding - 8 * 5)
    line = str(host).ljust(host_padding)

    if host.status:
        return line + '   ' + host.status

    for stat in ['min', 'avg', 'max', 'stdev']:
        if host.results_summary[stat] is not None:
            line += f' {host.results_summary[stat]:>7.2f}'
        else:
            line += '     -  '

    if host.results_summary['loss'] is not None:
        line += f' {host.results_summary["loss"]:>5.0%}  '
    else:
        line += '    -   '

    return line + get_histogram(host, line_width - len(line))


def get_color(color, last_color=None):
    '''Returns the ANSI code for `color`, taking in mind `last_color` to skip
    unnecessary color codes. `color` can be green, red, yellow, and reset'''
    if color != last_color:
        return {
            'green': '\x1b[32m',
            'red': '\x1b[31m',
            'yellow': '\x1b[33m',
            'reset': '\x1b[39m',
        }.get(color, '')

    return ''


def get_histogram(host, length):
    '''Returns an ANSI-colored string representing the host's results.'''
    # Output optimization by only including color markers when the color changes
    line = ''
    length = max(length, HISTOGRAM_LENGTH_MINIMUM)
    last_color = None

    for result in list(host.results)[-length:]:
        if result['latency'] == -1:
            line += get_color('red', last_color) + '.'
            last_color = 'red'
        else:
            color = 'green' if not result['error'] else 'yellow'
            line += get_color(color, last_color) + '!'
            last_color = color

    # Include a color reset at the end of the line
    return line + get_color('reset')


def get_table(hosts, all_hosts=False):
    '''Returns a table (string) of the hosts' results.

    Args:
        hosts (list): A list... of hosts... Yeah.
        all_hosts (bool): If `False`, table won't exceed screen's height.
    '''
    table = ''
    term_size = shutil.get_terminal_size()
    host_padding = max(len(str(x)) for x in hosts) + 1

    for index, host in enumerate(hosts):
        # Not printing all hosts and lines limit reached
        if not all_hosts and index >= term_size.lines - 1:
            break

        host_line = format_host(host, host_padding, term_size.columns)

        # Clear to end of line
        table += host_line + '\x1b[K\n'

    # Clear to end of screen
    table += '\x1b[J'

    # Not printing all hosts and some hosts overflowed
    if not all_hosts and len(hosts) > term_size.lines - 1:
        table += f'+{len(hosts) - (term_size.lines - 1)} more'

    return table