view rhodecode/lib/vcs/utils/progressbar.py @ 3774:60335b702a00 beta

invalidation: don't create CacheInvalidation records on startup Creating the records early gave an advantage before lightweight was introduced. With lightweight it is no longer necessary. The records will be created on demand anyway and there is no reason to create and maintain them before they are used.
author Mads Kiilerich <madski@unity3d.com>
date Wed, 03 Apr 2013 15:56:12 +0200
parents 324ac367a4da
children d7488551578e
line wrap: on
line source

# encoding: UTF-8
import sys
import datetime
from string import Template
from rhodecode.lib.vcs.utils.filesize import filesizeformat
from rhodecode.lib.vcs.utils.helpers import get_total_seconds


class ProgressBarError(Exception):
    pass

class AlreadyFinishedError(ProgressBarError):
    pass


class ProgressBar(object):

    default_elements = ['percentage', 'bar', 'steps']

    def __init__(self, steps=100, stream=None, elements=None):
        self.step = 0
        self.steps = steps
        self.stream = stream or sys.stderr
        self.bar_char = '='
        self.width = 50
        self.separator = ' | '
        self.elements = elements or self.default_elements
        self.started = None
        self.finished = False
        self.steps_label = 'Step'
        self.time_label = 'Time'
        self.eta_label = 'ETA'
        self.speed_label = 'Speed'
        self.transfer_label = 'Transfer'

    def __str__(self):
        return self.get_line()

    def __iter__(self):
        start = self.step
        end = self.steps + 1
        for x in xrange(start, end):
            self.render(x)
            yield x

    def get_separator(self):
        return self.separator

    def get_bar_char(self):
        return self.bar_char

    def get_bar(self):
        char = self.get_bar_char()
        perc = self.get_percentage()
        length = int(self.width * perc / 100)
        bar = char * length
        bar = bar.ljust(self.width)
        return bar

    def get_elements(self):
        return self.elements

    def get_template(self):
        separator = self.get_separator()
        elements = self.get_elements()
        return Template(separator.join((('$%s' % e) for e in elements)))

    def get_total_time(self, current_time=None):
        if current_time is None:
            current_time = datetime.datetime.now()
        if not self.started:
            return datetime.timedelta()
        return current_time - self.started

    def get_rendered_total_time(self):
        delta = self.get_total_time()
        if not delta:
            ttime = '-'
        else:
            ttime = str(delta)
        return '%s %s' % (self.time_label, ttime)

    def get_eta(self, current_time=None):
        if current_time is None:
            current_time = datetime.datetime.now()
        if self.step == 0:
            return datetime.timedelta()
        total_seconds = get_total_seconds(self.get_total_time())
        eta_seconds = total_seconds * self.steps / self.step - total_seconds
        return datetime.timedelta(seconds=int(eta_seconds))

    def get_rendered_eta(self):
        eta = self.get_eta()
        if not eta:
            eta = '--:--:--'
        else:
            eta = str(eta).rjust(8)
        return '%s: %s' % (self.eta_label, eta)

    def get_percentage(self):
        return float(self.step) / self.steps * 100

    def get_rendered_percentage(self):
        perc = self.get_percentage()
        return ('%s%%' % (int(perc))).rjust(5)

    def get_rendered_steps(self):
        return '%s: %s/%s' % (self.steps_label, self.step, self.steps)

    def get_rendered_speed(self, step=None, total_seconds=None):
        if step is None:
            step = self.step
        if total_seconds is None:
            total_seconds = get_total_seconds(self.get_total_time())
        if step <= 0 or total_seconds <= 0:
            speed = '-'
        else:
            speed = filesizeformat(float(step) / total_seconds)
        return '%s: %s/s' % (self.speed_label, speed)

    def get_rendered_transfer(self, step=None, steps=None):
        if step is None:
            step = self.step
        if steps is None:
            steps = self.steps

        if steps <= 0:
            return '%s: -' % self.transfer_label
        total = filesizeformat(float(steps))
        if step <= 0:
            transferred = '-'
        else:
            transferred = filesizeformat(float(step))
        return '%s: %s / %s' % (self.transfer_label, transferred, total)

    def get_context(self):
        return {
            'percentage': self.get_rendered_percentage(),
            'bar': self.get_bar(),
            'steps': self.get_rendered_steps(),
            'time': self.get_rendered_total_time(),
            'eta': self.get_rendered_eta(),
            'speed': self.get_rendered_speed(),
            'transfer': self.get_rendered_transfer(),
        }

    def get_line(self):
        template = self.get_template()
        context = self.get_context()
        return template.safe_substitute(**context)

    def write(self, data):
        self.stream.write(data)

    def render(self, step):
        if not self.started:
            self.started = datetime.datetime.now()
        if self.finished:
            raise AlreadyFinishedError
        self.step = step
        self.write('\r%s' % self)
        if step == self.steps:
            self.finished = True
        if step == self.steps:
            self.write('\n')


"""
termcolors.py

Grabbed from Django (http://www.djangoproject.com)
"""

color_names = ('black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white')
foreground = dict([(color_names[x], '3%s' % x) for x in range(8)])
background = dict([(color_names[x], '4%s' % x) for x in range(8)])

RESET = '0'
opt_dict = {'bold': '1', 'underscore': '4', 'blink': '5', 'reverse': '7', 'conceal': '8'}

def colorize(text='', opts=(), **kwargs):
    """
    Returns your text, enclosed in ANSI graphics codes.

    Depends on the keyword arguments 'fg' and 'bg', and the contents of
    the opts tuple/list.

    Returns the RESET code if no parameters are given.

    Valid colors:
        'black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white'

    Valid options:
        'bold'
        'underscore'
        'blink'
        'reverse'
        'conceal'
        'noreset' - string will not be auto-terminated with the RESET code

    Examples:
        colorize('hello', fg='red', bg='blue', opts=('blink',))
        colorize()
        colorize('goodbye', opts=('underscore',))
        print colorize('first line', fg='red', opts=('noreset',))
        print 'this should be red too'
        print colorize('and so should this')
        print 'this should not be red'
    """
    code_list = []
    if text == '' and len(opts) == 1 and opts[0] == 'reset':
        return '\x1b[%sm' % RESET
    for k, v in kwargs.iteritems():
        if k == 'fg':
            code_list.append(foreground[v])
        elif k == 'bg':
            code_list.append(background[v])
    for o in opts:
        if o in opt_dict:
            code_list.append(opt_dict[o])
    if 'noreset' not in opts:
        text = text + '\x1b[%sm' % RESET
    return ('\x1b[%sm' % ';'.join(code_list)) + text

def make_style(opts=(), **kwargs):
    """
    Returns a function with default parameters for colorize()

    Example:
        bold_red = make_style(opts=('bold',), fg='red')
        print bold_red('hello')
        KEYWORD = make_style(fg='yellow')
        COMMENT = make_style(fg='blue', opts=('bold',))
    """
    return lambda text: colorize(text, opts, **kwargs)

NOCOLOR_PALETTE = 'nocolor'
DARK_PALETTE = 'dark'
LIGHT_PALETTE = 'light'

PALETTES = {
    NOCOLOR_PALETTE: {
        'ERROR':        {},
        'NOTICE':       {},
        'SQL_FIELD':    {},
        'SQL_COLTYPE':  {},
        'SQL_KEYWORD':  {},
        'SQL_TABLE':    {},
        'HTTP_INFO':         {},
        'HTTP_SUCCESS':      {},
        'HTTP_REDIRECT':     {},
        'HTTP_NOT_MODIFIED': {},
        'HTTP_BAD_REQUEST':  {},
        'HTTP_NOT_FOUND':    {},
        'HTTP_SERVER_ERROR': {},
    },
    DARK_PALETTE: {
        'ERROR':        { 'fg': 'red', 'opts': ('bold',) },
        'NOTICE':       { 'fg': 'red' },
        'SQL_FIELD':    { 'fg': 'green', 'opts': ('bold',) },
        'SQL_COLTYPE':  { 'fg': 'green' },
        'SQL_KEYWORD':  { 'fg': 'yellow' },
        'SQL_TABLE':    { 'opts': ('bold',) },
        'HTTP_INFO':         { 'opts': ('bold',) },
        'HTTP_SUCCESS':      { },
        'HTTP_REDIRECT':     { 'fg': 'green' },
        'HTTP_NOT_MODIFIED': { 'fg': 'cyan' },
        'HTTP_BAD_REQUEST':  { 'fg': 'red', 'opts': ('bold',) },
        'HTTP_NOT_FOUND':    { 'fg': 'yellow' },
        'HTTP_SERVER_ERROR': { 'fg': 'magenta', 'opts': ('bold',) },
    },
    LIGHT_PALETTE: {
        'ERROR':        { 'fg': 'red', 'opts': ('bold',) },
        'NOTICE':       { 'fg': 'red' },
        'SQL_FIELD':    { 'fg': 'green', 'opts': ('bold',) },
        'SQL_COLTYPE':  { 'fg': 'green' },
        'SQL_KEYWORD':  { 'fg': 'blue' },
        'SQL_TABLE':    { 'opts': ('bold',) },
        'HTTP_INFO':         { 'opts': ('bold',) },
        'HTTP_SUCCESS':      { },
        'HTTP_REDIRECT':     { 'fg': 'green', 'opts': ('bold',) },
        'HTTP_NOT_MODIFIED': { 'fg': 'green' },
        'HTTP_BAD_REQUEST':  { 'fg': 'red', 'opts': ('bold',) },
        'HTTP_NOT_FOUND':    { 'fg': 'red' },
        'HTTP_SERVER_ERROR': { 'fg': 'magenta', 'opts': ('bold',) },
    }
}
DEFAULT_PALETTE = DARK_PALETTE

# ---------------------------- #
# --- End of termcolors.py --- #
# ---------------------------- #


class ColoredProgressBar(ProgressBar):

    BAR_COLORS = (
        (10, 'red'),
        (30, 'magenta'),
        (50, 'yellow'),
        (99, 'green'),
        (100, 'blue'),
    )

    def get_line(self):
        line = super(ColoredProgressBar, self).get_line()
        perc = self.get_percentage()
        if perc > 100:
            color = 'blue'
        for max_perc, color in self.BAR_COLORS:
            if perc <= max_perc:
                break
        return colorize(line, fg=color)


class AnimatedProgressBar(ProgressBar):

    def get_bar_char(self):
        chars = '-/|\\'
        if self.step >= self.steps:
            return '='
        return chars[self.step % len(chars)]


class BarOnlyProgressBar(ProgressBar):

    default_elements = ['bar', 'steps']

    def get_bar(self):
        bar = super(BarOnlyProgressBar, self).get_bar()
        perc = self.get_percentage()
        perc_text = '%s%%' % int(perc)
        text = (' %s%% ' % (perc_text)).center(self.width, '=')
        L = text.find(' ')
        R = text.rfind(' ')
        bar = ' '.join((bar[:L], perc_text, bar[R:]))
        return bar


class AnimatedColoredProgressBar(AnimatedProgressBar,
                                 ColoredProgressBar):
    pass


class BarOnlyColoredProgressBar(ColoredProgressBar,
                                BarOnlyProgressBar):
    pass



def main():
    import time

    print "Standard progress bar..."
    bar = ProgressBar(30)
    for x in xrange(1, 31):
            bar.render(x)
            time.sleep(0.02)
    bar.stream.write('\n')
    print

    print "Empty bar..."
    bar = ProgressBar(50)
    bar.render(0)
    print
    print

    print "Colored bar..."
    bar = ColoredProgressBar(20)
    for x in bar:
        time.sleep(0.01)
    print

    print "Animated char bar..."
    bar = AnimatedProgressBar(20)
    for x in bar:
        time.sleep(0.01)
    print

    print "Animated + colored char bar..."
    bar = AnimatedColoredProgressBar(20)
    for x in bar:
        time.sleep(0.01)
    print

    print "Bar only ..."
    bar = BarOnlyProgressBar(20)
    for x in bar:
        time.sleep(0.01)
    print

    print "Colored, longer bar-only, eta, total time ..."
    bar = BarOnlyColoredProgressBar(40)
    bar.width = 60
    bar.elements += ['time', 'eta']
    for x in bar:
        time.sleep(0.01)
    print
    print

    print "File transfer bar, breaks after 2 seconds ..."
    total_bytes = 1024 * 1024 * 2
    bar = ProgressBar(total_bytes)
    bar.width = 50
    bar.elements.remove('steps')
    bar.elements += ['transfer', 'time', 'eta', 'speed']
    for x in xrange(0, bar.steps, 1024):
        bar.render(x)
        time.sleep(0.01)
        now = datetime.datetime.now()
        if now - bar.started >= datetime.timedelta(seconds=2):
            break
    print
    print



if __name__ == '__main__':
    main()